Redux中修改列表项时如何仅渲染变更项?
解决Redux Todo列表仅修改项重渲染的问题
你遇到的问题核心是组件重渲染触发条件没控制好,虽然用了memo,但因为props传递的内容不稳定,或者组件依赖的状态范围太大,导致整个列表都跟着重渲染。以下是具体的解决思路和代码修改方案:
问题根源分析
- List组件的
todoList引用变化:每次修改todo的状态,Redux Toolkit会生成新的数组引用(immer结构共享的特性),导致List组件必然重渲染。 - TodoItem的props不稳定:如果传递的
setDone是每次渲染都生成的新函数,或者todo对象虽然没修改但因为父组件重渲染被重新传递,memo的浅比较会认为props变化,触发所有TodoItem重渲染。
解决方案一:让TodoItem独立获取自身数据(推荐)
让List只传递todo的id给TodoItem,TodoItem内部通过useSelector根据id获取对应的数据,这样List的重渲染不会影响未修改的TodoItem,只有目标TodoItem会因为数据变化重渲染。
修改TodoItem组件
import { useDispatch, useSelector } from 'react-redux'; import { setDone } from './todoSlice'; // 导入你的action function TodoItem({ id }: { id: string }) { // 只获取当前id对应的todo const todo = useSelector((state: RootState) => state.todo.find(item => item.id === id)); const dispatch = useDispatch(); if (!todo) return null; return ( <Row justify='space-between'> <Checkbox checked={todo.done} onChange={(e) => dispatch(setDone([id, e.target.checked]))} > <span style={todo.done ? { textDecoration: 'line-through' } : {}}> {todo.todo} </span> </Checkbox> </Row> ); } export default memo(TodoItem);
修改List组件
function List() { const todoList = useSelector((state: RootState) => state.todo); return ( <> {todoList.map(todo => ( <TodoItem key={todo.id} id={todo.id} /> ))} </> ); } export default memo(List); // 这个memo可以考虑去掉,因为List渲染成本极低
解决方案二:稳定传递的props(如果坚持传递todo和setDone)
如果不想改变数据获取方式,需要保证传递给TodoItem的props引用稳定:
- 用
useCallback包裹setDone函数:在传递setDone的父组件中,用useCallback确保函数引用不变。 - 确保未修改的todo对象引用不变:RTK的immer已经保证了这一点,只要reducer写法正确,未修改的todo会保持原引用。
父组件中处理setDone
import { useDispatch, useCallback } from 'react-redux'; import { setDone } from './todoSlice'; function ParentComponent() { const dispatch = useDispatch(); // 用useCallback稳定函数引用 const handleSetDone = useCallback((id: string, done: boolean) => { dispatch(setDone([id, done])); }, [dispatch]); return <List setDone={handleSetDone} />; }
List组件修正(原代码中的props.todoList是笔误,应该用自己获取的todoList)
function List({ setDone }: { setDone: (id: string, done: boolean) => void }) { const todoList = useSelector((state: RootState) => state.todo); return ( <> {todoList.map(todo => ( <TodoItem key={todo.id} todo={todo} setDone={setDone} /> ))} </> ); } export default memo(List);
验证效果
修改后,点击某条todo的复选框,只有该TodoItem会重渲染,其他项不会有渲染触发。可以通过React DevTools的"Highlight Updates"功能来验证。
内容的提问来源于stack exchange,提问作者mukamuka
相关产品推荐
相关产品推荐

