在 React.js 的另一个组件中修改一个组件的 HTML 元素
Modify an HTML element from one component in another component in React.js
我创建了 todoapp,我想创建一个按钮,单击该按钮时,将修改列表以仅显示已完成的任务。
我有三个组成部分:
App.js
- 它是一个主要组件,TasksList.js
- 负责显示任务(最初它显示所有任务),ListHandler
- 我想在这里创建上述按钮。
我决定使用 useRef 来在任何地方访问任务列表(ul with class tasksWrapper in TasksList
)。
App.js
:
import React, { useState, useRef } from "react";
import "./sass/main.css";
import Header from "./components/Header";
import Browser from "./components/Browser";
import TasksList from "./components/TasksList";
import ListHandler from "./components/ListHandler";
function App() {
//set default date for initial tasks
let defaultDate = new Date().toTimeString();
defaultDate = defaultDate.split(" ")[0];
const [tasksList, setTasksList] = useState([
{
id: 0,
content: "embrace Java",
done: false,
active: true,
date: defaultDate,
},
{
id: 1,
content: "find a job in my dream industry",
done: true,
active: true,
date: defaultDate,
},
{
id: 2,
content: "win a war",
done: false,
active: true,
date: defaultDate,
},
]);
const tasksListWrapper = useRef();
return (
<div className="App">
<Header />
<Browser tasksList={tasksList} />
<TasksList
tasksList={tasksList}
setTasksList={setTasksList}
tasksListWrapper={tasksListWrapper} />
<ListHandler
tasksList={tasksList}
setTasksList={setTasksList}
tasksListWrapper={tasksListWrapper} />
</div>
);
}
export default App;
TasksList.js
返回 JSX(displayTasks("all")
只是 returns < li > 每个任务 task.content :
return (
<section className="tasksList">
<h2>LIST</h2>
<ul ref={tasksListWrapper} className="tasksWrapper">{displayTasks("all")}
</ul>
</section>);
ListHandler
按钮和函数displayDoneTasks
负责显示已完成的任务:
const displayDoneTasks = () => {
const returnDoneTasks = () => {
return tasksList.map((task) => {
if (task.active && task.done) {
return (
<li
key={task.id}
className="task"
id={task.id}
>
{task.content}
</li>
);
} else return null;
});
};
console.log(returnDoneTasks());
tasksListWrapper.current.innerHTML = returnDoneTasks(); //if task.done === true then output is: {$$typeof: Symbol(react.element), type: "li", key: "0", ref: null, props: {…}, …}
}
return (
<section className="listHandler">
>
<button className="doneTasks" onClick={() => displayDoneTasks()}>
Show only completed tasks
</button>
</section>
);
为什么 {$$typeof: Symbol(react.element), type: "li", key: "0", ref: null, props: {…}, …}
没有显示为 HTML 个元素?也许我的解决方案是错误的,我应该用不同的方式来解决?
我认为您不能在不使用文档的情况下直接编辑另一个组件。但是,如果它是父项或子项,是否可以轻松地在 props 中发送方法,这些方法可用于修改状态,在这种情况下会重新呈现组件。
您看到的行为是因为您查看的是 React JSX 元素而不是 HTML DOM 个节点。
你这里的方法真的是错误的。你永远不想使用 .innerHTML
来覆盖你自己用 React 生成的其他内容。您想更新道具和状态,以便生成正确的内容。
事实上,您的 ListHandler
已经可以访问道具 setTasksList
,它可以用来更新 TasksList
显示的列表。在这种特殊情况下,这并不是您想要的 ,因为将 tasksList
设置为一个子集会导致其他任务丢失。但这是一般方法。
你在这里想要的是让共享父级 App
存储某种与过滤相关的状态。一个简单的 boolean
状态可能如下所示:
const [showDone, setShowDone] = useState(false);
为 TasksList
提供反映当前筛选(所有任务或已完成任务)的任务数组。为 ListHandler
提供一个可以调用以更新过滤器的函数。
您可以创建另一个状态变量 showCompleted
并允许子组件 (ListHandler
) 通过将函数作为属性传递来修改它。
import React, { useState, useRef } from "react";
import "./sass/main.css";
import Header from "./components/Header";
import Browser from "./components/Browser";
import TasksList from "./components/TasksList";
import ListHandler from "./components/ListHandler";
function App() {
//set default date for initial tasks
let defaultDate = new Date().toTimeString();
defaultDate = defaultDate.split(" ")[0];
const [tasksList, setTasksList] = useState([
{
id: 0,
content: "embrace Java",
done: false,
active: true,
date: defaultDate,
},
{
id: 1,
content: "find a job in my dream industry",
done: true,
active: true,
date: defaultDate,
},
{
id: 2,
content: "win a war",
done: false,
active: true,
date: defaultDate,
},
]);
// const tasksListWrapper = useRef(); not needed
const [showCompleted, setShowCompleted] = useState(false);
// display all tasks or only completed based on showCompleted state
const tasksToShow = !showCompleted ? tasksList : tasksList.filter(
task => task.active && task.done
);
return (
<div className="App">
<Header />
<Browser tasksList={tasksToShow} />
<TasksList
tasksList={tasksToShow}
setTasksList={setTasksList} />
<ListHandler
tasksList={tasksToShow}
setTasksList={setTasksList}
setShowCompleted={setShowCompleted} />
</div>
);
}
export default App;
ListHandler
需要调用作为 setShowCompleted
prop.
传递的函数
function ListHandler({ setShowCompleted }) {
return (
<section className="listHandler">
<button className="doneTasks" onClick={() => setShowCompleted(true)}>
Show only completed tasks
</button>
</section>
);
}
它将导致 App
中的状态更新,并将通过仅选择那些已完成的任务来重新计算 tasksToShow
,这些任务又将传递给 TasksList
进行渲染。
我创建了 todoapp,我想创建一个按钮,单击该按钮时,将修改列表以仅显示已完成的任务。
我有三个组成部分:
App.js
- 它是一个主要组件,TasksList.js
- 负责显示任务(最初它显示所有任务),ListHandler
- 我想在这里创建上述按钮。
我决定使用 useRef 来在任何地方访问任务列表(ul with class tasksWrapper in TasksList
)。
App.js
:
import React, { useState, useRef } from "react";
import "./sass/main.css";
import Header from "./components/Header";
import Browser from "./components/Browser";
import TasksList from "./components/TasksList";
import ListHandler from "./components/ListHandler";
function App() {
//set default date for initial tasks
let defaultDate = new Date().toTimeString();
defaultDate = defaultDate.split(" ")[0];
const [tasksList, setTasksList] = useState([
{
id: 0,
content: "embrace Java",
done: false,
active: true,
date: defaultDate,
},
{
id: 1,
content: "find a job in my dream industry",
done: true,
active: true,
date: defaultDate,
},
{
id: 2,
content: "win a war",
done: false,
active: true,
date: defaultDate,
},
]);
const tasksListWrapper = useRef();
return (
<div className="App">
<Header />
<Browser tasksList={tasksList} />
<TasksList
tasksList={tasksList}
setTasksList={setTasksList}
tasksListWrapper={tasksListWrapper} />
<ListHandler
tasksList={tasksList}
setTasksList={setTasksList}
tasksListWrapper={tasksListWrapper} />
</div>
);
}
export default App;
TasksList.js
返回 JSX(displayTasks("all")
只是 returns < li > 每个任务 task.content :
return (
<section className="tasksList">
<h2>LIST</h2>
<ul ref={tasksListWrapper} className="tasksWrapper">{displayTasks("all")}
</ul>
</section>);
ListHandler
按钮和函数displayDoneTasks
负责显示已完成的任务:
const displayDoneTasks = () => {
const returnDoneTasks = () => {
return tasksList.map((task) => {
if (task.active && task.done) {
return (
<li
key={task.id}
className="task"
id={task.id}
>
{task.content}
</li>
);
} else return null;
});
};
console.log(returnDoneTasks());
tasksListWrapper.current.innerHTML = returnDoneTasks(); //if task.done === true then output is: {$$typeof: Symbol(react.element), type: "li", key: "0", ref: null, props: {…}, …}
}
return (
<section className="listHandler">
>
<button className="doneTasks" onClick={() => displayDoneTasks()}>
Show only completed tasks
</button>
</section>
);
为什么 {$$typeof: Symbol(react.element), type: "li", key: "0", ref: null, props: {…}, …}
没有显示为 HTML 个元素?也许我的解决方案是错误的,我应该用不同的方式来解决?
我认为您不能在不使用文档的情况下直接编辑另一个组件。但是,如果它是父项或子项,是否可以轻松地在 props 中发送方法,这些方法可用于修改状态,在这种情况下会重新呈现组件。
您看到的行为是因为您查看的是 React JSX 元素而不是 HTML DOM 个节点。
你这里的方法真的是错误的。你永远不想使用 .innerHTML
来覆盖你自己用 React 生成的其他内容。您想更新道具和状态,以便生成正确的内容。
事实上,您的 ListHandler
已经可以访问道具 setTasksList
,它可以用来更新 TasksList
显示的列表。在这种特殊情况下,这并不是您想要的 ,因为将 tasksList
设置为一个子集会导致其他任务丢失。但这是一般方法。
你在这里想要的是让共享父级 App
存储某种与过滤相关的状态。一个简单的 boolean
状态可能如下所示:
const [showDone, setShowDone] = useState(false);
为 TasksList
提供反映当前筛选(所有任务或已完成任务)的任务数组。为 ListHandler
提供一个可以调用以更新过滤器的函数。
您可以创建另一个状态变量 showCompleted
并允许子组件 (ListHandler
) 通过将函数作为属性传递来修改它。
import React, { useState, useRef } from "react";
import "./sass/main.css";
import Header from "./components/Header";
import Browser from "./components/Browser";
import TasksList from "./components/TasksList";
import ListHandler from "./components/ListHandler";
function App() {
//set default date for initial tasks
let defaultDate = new Date().toTimeString();
defaultDate = defaultDate.split(" ")[0];
const [tasksList, setTasksList] = useState([
{
id: 0,
content: "embrace Java",
done: false,
active: true,
date: defaultDate,
},
{
id: 1,
content: "find a job in my dream industry",
done: true,
active: true,
date: defaultDate,
},
{
id: 2,
content: "win a war",
done: false,
active: true,
date: defaultDate,
},
]);
// const tasksListWrapper = useRef(); not needed
const [showCompleted, setShowCompleted] = useState(false);
// display all tasks or only completed based on showCompleted state
const tasksToShow = !showCompleted ? tasksList : tasksList.filter(
task => task.active && task.done
);
return (
<div className="App">
<Header />
<Browser tasksList={tasksToShow} />
<TasksList
tasksList={tasksToShow}
setTasksList={setTasksList} />
<ListHandler
tasksList={tasksToShow}
setTasksList={setTasksList}
setShowCompleted={setShowCompleted} />
</div>
);
}
export default App;
ListHandler
需要调用作为 setShowCompleted
prop.
function ListHandler({ setShowCompleted }) {
return (
<section className="listHandler">
<button className="doneTasks" onClick={() => setShowCompleted(true)}>
Show only completed tasks
</button>
</section>
);
}
它将导致 App
中的状态更新,并将通过仅选择那些已完成的任务来重新计算 tasksToShow
,这些任务又将传递给 TasksList
进行渲染。