React父组件State嵌套更新时重置为初始值问题求助
问题原因与解决方案
核心问题是闭包导致的陈旧状态(stale state),以及setBlocklist的错误用法:
1. 闭包捕获陈旧状态
父组件的updateBlockList函数在初始渲染时创建,捕获的是初始的blockList状态。后续父组件状态更新后,该函数未重新创建(无依赖触发更新),导致每次执行blockList.filter时,使用的始终是初始数组,状态自然回滚。
2. setBlocklist用法错误
你在setBlocklist中混用了直接状态值和函数式更新,不仅参数命名冲突,还浪费了函数式更新能获取最新状态的特性——完全不需要先基于当前blockList过滤,再传入更新函数。
修正后的代码
父组件(DragDrop)
function DragDrop() { const [blockList, setBlocklist] = useState(initblockList); // 改用函数式更新,直接获取最新状态 const updateBlockList = (id) => { setBlocklist(prevBlockList => prevBlockList.filter(block => block.id !== id)); } return ( <> <AboveBoard blockList={blockList} updateBlockList={updateBlockList} /> <div className="blocks"> {blockList.map((img) => { // 用id作为key,比索引更稳定 return <Block key={img.id} id={img.id} url={img.url} />; })} </div> </> ); } export default DragDrop;
子组件(AboveBoard)
function AboveBoard({ blockList, updateBlockList }) { const [board, setBoard] = useState([]); const [{ isOver }, drop] = useDrop(() => ({ accept: "block", drop: (item) => addBlockToBoard(item.id), collect: (monitor) => ({ isOver: !!monitor.isOver(), }) })); const addBlockToBoard = (id) => { // 用find替代filter,仅匹配单个项更高效 const currentBlock = blockList.find(block => block.id === id); setBoard(prevBoard => [...prevBoard, currentBlock]); // 仅传递id给父组件即可完成过滤 updateBlockList(id); } return ( <div className="aboveboard" ref={drop}> {board.map((img) => { return <UnBlock key={img.id} id={img.id} url={img.url} />; })} </div> ); } export default AboveBoard;
额外优化点
- 使用
find替代filter获取单个区块,语义更清晰且性能更好 - 用数据的
id作为组件key,避免数组变化时索引不稳定引发的渲染异常 - 移除了原代码中未定义的
setAbove无效prop
内容的提问来源于stack exchange,提问作者Craig Bauer
相关产品推荐
相关产品推荐

