这是一个更简洁的版本我之前提出的一个问题 https://stackoverflow.com/questions/69240704/rendering-of-children-components-triggers-unwanted-unmounts。希望它能得到更好的解释和更容易理解。
这是一个小应用程序,有 3 个需要输入数字的输入(请忽略您也可以输入非数字,这不是重点)。它计算所有显示数字的总和。如果您将其中一个输入更改为另一个数字,则总和会更新。
这是它的代码:
import { useCallback, useEffect, useState } from 'react';
function App() {
const [items, setItems] = useState([
{ index: 0, value: "1" },
{ index: 1, value: "2" },
{ index: 2, value: "3" },
]);
const callback = useCallback((item) => {
let newItems = [...items];
newItems[item.index] = item;
setItems(newItems);
}, [items]);
return (
<div>
<SumItems items={items} />
<ul>
{items.map((item) =>
<ListItem key={item.index} item={item} callback={callback} />
)}
</ul>
</div>
);
}
function ListItem(props) {
const [item, setItem] = useState(props.item);
useEffect(() => {
console.log("ListItem ", item.index, " mounting");
})
useEffect(() => {
return () => console.log("ListItem ", item.index, " unmounting");
});
useEffect(() => {
console.log("ListItem ", item.index, " updated");
}, [item]);
const onInputChange = (event) => {
const newItem = { ...item, value: event.target.value };
setItem(newItem);
props.callback(newItem);
}
return (
<div>
<input type="text" value={item.value} onChange={onInputChange} />
</div>);
};
function SumItems(props) {
return (
<div>Sum : {props.items.reduce((total, item) => total + parseInt(item.value), 0)}</div>
)
}
export default App;
这是启动时以及将第二个输入 2 更改为 4 后的控制台输出:
ListItem 0 mounting App.js:35
ListItem 0 updated App.js:43
ListItem 1 mounting App.js:35
ListItem 1 updated App.js:43
ListItem 2 mounting App.js:35
ListItem 2 updated App.js:43
ListItem 0 unmounting react_devtools_backend.js:4049:25
ListItem 1 unmounting react_devtools_backend.js:4049:25
ListItem 2 unmounting react_devtools_backend.js:4049:25
ListItem 0 mounting react_devtools_backend.js:4049:25
ListItem 1 mounting react_devtools_backend.js:4049:25
ListItem 1 updated react_devtools_backend.js:4049:25
ListItem 2 mounting react_devtools_backend.js:4049:25
正如您所看到的,当更新单个输入时,所有子级都不会重新渲染,它们首先被卸载,然后重新安装。太浪费了,所有的输入都已经处于正确的状态,只需要更新总和。想象一下有数百个这样的输入。
如果这只是重新渲染的问题,我可以考虑记忆。但这是行不通的,因为callback
更新正是因为items
改变。不,我的问题是关于卸载all这些孩子。
问题1: 可以避免卸载吗?
如果我相信肯特·C·多兹 (Kent C. Dodds) 撰写的这篇文章 https://kentcdodds.com/blog/understanding-reacts-key-prop,答案是否定的(强调我的):
React 的 key prop 使您能够控制组件实例。
每次 React 渲染你的组件时,它都会调用你的函数
检索用于更新 DOM 的新 React 元素。如果
你返回相同的元素类型,它保留那些组件/DOM 节点
周围,即使所有*道具都改变了。
(...)
例外的是关键道具。这允许您返回
完全相同的元素类型,但是强制 React 卸载之前的
实例,然后安装一个新的。这意味着所有拥有
当时存在于组件中的内容被完全删除,并且
出于所有意图和目的,组件被“重新初始化”。
问题2:如果这是真的,那么我应该考虑什么设计来避免看似不必要的事情并导致问题在我真实的 https://stackoverflow.com/questions/69240704/rendering-of-children-components-triggers-unwanted-unmountsapp 是因为每个输入组件中都发生异步处理?