我有一个简单的项目列表,可以自行更新。更新一个项目会触发所有项目的重新渲染。我为项目提供了唯一键,我希望 React 会跳过未更改项目的更新。即使 App
重新创建 items
和 handleItemUpdate
函数。
怎么了?
<块引用>Codepen 示例:https://codepen.io/enepom/pen/VwKdxZN
点击一项会在控制台中打印 3 个项目渲染,而不是一个。
项目组件:
const Item = React.memo(({ id, count, onUpdate }) => {
console.log('> ITEM RENDER', id);
const handleClick = () => {
onUpdate(id, count + 1);
};
return (
<li onClick={handleClick}>{id}: {count}</li>
);
});
应用组件:
const App = () => {
const [items, setItems] = React.useState([]);
React.useEffect(() => {
setItems([
{ id: 'id1', count: 7 },
{ id: 'id2', count: 8 },
{ id: 'id3', count: 9 },
]);
}, []);
const handleItemUpdate = React.useCallback((itemId, count) => {
const itemIndex = items.findIndex(item => item.id === itemId);
if (itemIndex > -1) {
const itemsCopy = items.slice();
itemsCopy[itemIndex].count = count;
setItems(itemsCopy);
}
}, [items, setItems]);
return (
<ul>
{items.map(item => (
<Item key={item.id} id={item.id} count={item.count} onUpdate={handleItemUpdate} />
))}
</ul>
);
};
答案 0 :(得分:1)
每次更新项目时,都会重新计算 handleItemUpdate
,因为 items
已更改,并且在 useCallback 依赖项数组中:
[items, setItems]
因此,当一个属性 (onUpdate) 发生更改时,每个 Item
都会再次呈现。
答案 1 :(得分:1)
items
中的 handleItemUpdate
。此代码按预期工作:
const handleItemUpdate = React.useCallback((itemId, count) => {
setItems(prevItems => prevItems.map(item =>
item.id === itemId
? { ...item, count }
: item
));
}, [setItems]);
答案 2 :(得分:0)
这是您可以做到的一种方法。 React.memo 进行了浅层比较,因为您传递的对象可能通过引用而不同,即使值相同,您也需要使用自定义逻辑实现 arePropsEqual。
const Item = ({ id, count, onUpdate }) => {
console.log('> ITEM RENDER', id);
const handleClick = () => {
onUpdate(id, count + 1);
};
return (
<li onClick={handleClick}>{id}: {count}</li>
);
};
const arePropsEqual =(next,prev)=>{
return next.id ===prev.id && next.count ===prev.count
}
const MemoItem = React.memo(Item,arePropsEqual);
const App = () => {
const [items, setItems] = React.useState([]);
React.useEffect(() => {
setItems([
{ id: 'id1', count: 7 },
{ id: 'id2', count: 8 },
{ id: 'id3', count: 9 },
]);
}, []);
const handleItemUpdate = React.useCallback((itemId, count) => {
const itemIndex = items.findIndex(item => item.id === itemId);
if (itemIndex > -1) {
const itemsCopy = items.slice();
itemsCopy[itemIndex].count = count;
setItems(itemsCopy);
}
}, [items, setItems]);
return (
<ul>
{items.map(item => (
<MemoItem key={item.id} id={item.id} count={item.count} onUpdate={handleItemUpdate} />
))}
</ul>
);
};
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
答案 3 :(得分:0)
这是一种避免不必要渲染的方法:
向 React.memo 添加相等函数:
const Item = React.memo(({ id, count, onUpdate,items }) => {
console.log('> ITEM RENDER', id);
const handleClick = () => {
onUpdate(id, count + 1,items);
};
return (
<li onClick={handleClick}>{id}: {count}</li>
);
}, (prev, next) => prev.id===next.id && prev.count===next.count);
const App = () => {
const [items, setItems] = React.useState([]);
React.useEffect(() => {
setItems([
{ id: 'id1', count: 7 },
{ id: 'id2', count: 8 },
{ id: 'id3', count: 9 },
]);
}, []);
const handleItemUpdate = React.useCallback((itemId, count,items) => {
const itemIndex = items.findIndex(item => item.id === itemId);
if (itemIndex > -1) {
const itemsCopy = items.slice();
itemsCopy[itemIndex].count = count;
setItems(itemsCopy);
}
}, []);
return (
<ul>
{items.map(item => (
<Item key={item.id} id={item.id} count={item.count} onUpdate={handleItemUpdate} items={items}/>
))}
</ul>
);
};
ReactDOM.render(<App />, document.getElementById("root"));