React渲染正常但createNote函数内读取的state值不正确
React 状态闭包问题:创建笔记时读取到旧状态值
问题复现代码
useEffect(() => { getNotes() }, []); const getNotes = async () => { const notes = await fetch(`${getNotesPath()}`); const notesData = await notes.json(); setEstimateNotes([...notesData]); }; const createNote = async () => { console.log(estimateNotes); // 打印空数组 let noteParams = { ... // 参数逻辑正常,笔记可成功写入数据库 }; const url = `${getNotesPath()}/create`; const body = { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ ...noteParams, }), }; const newNote = await fetch(url, body); const newNoteData = await newNote.json(); setEstimateNotes([...estimateNotes, newNoteData]) // 最终仅渲染1条新笔记 }; const destroyNote = async (id) => { // 功能完全正常 const destroyedNote = await fetch(`/api/notes/${id}/destroy`); const destroyedNoteData = await destroyedNote.json(); setEstimateNotes( [...estimateNotes].filter((note) => note.id !== destroyedNoteData.id) ); }; return estimateNotes.map((note, i) => { return ( <div key={`notes-${i}`} className="note"> {/* 笔记内容渲染正常 */} </div> ); });
问题现象
- 页面初始加载后笔记列表渲染正常,所有已有笔记均可正确展示
- 执行
createNote时,函数内部读取estimateNotes得到空数组 createNote中追加新笔记的状态更新始终基于空数组执行,更新后页面仅展示最新创建的1条笔记- 逻辑类似的
destroyNote函数读取、更新同一状态完全正常
根本原因
这是典型的React函数组件闭包陷阱:createNote函数捕获的是组件首次渲染时的estimateNotes初始值(空数组)。如果该函数被空依赖的useCallback缓存、或者绑定到了初次渲染生成且未更新的DOM事件上,后续组件重渲染时,触发调用的始终是持有旧闭包的createNote版本,自然读不到更新后的状态值。destroyNote能正常运行,是因为它是在每次列表重渲染时,为每个笔记项单独绑定的新函数,每次重渲染生成的destroyNote都会捕获到当前最新的estimateNotes值,不会出现闭包陈旧的问题。
修复方案
优先使用函数式状态更新,完全规避闭包依赖问题,不需要依赖当前作用域下的状态变量,React会自动将最新的状态值传入更新回调:
- 修改
createNote中的状态更新逻辑:
// 替换原有setEstimateNotes([...estimateNotes, newNoteData]) setEstimateNotes(prevNotes => [...prevNotes, newNoteData])
- 建议同步修改
destroyNote的状态更新逻辑,避免后续调整代码时出现同类问题:
setEstimateNotes(prevNotes => prevNotes.filter(note => note.id !== destroyedNoteData.id))
如果使用useCallback包裹事件函数,必须将函数内用到的所有状态、props加入依赖数组,保证函数能获取到最新值,但该方案维护成本高于函数式更新。
内容的提问来源于stack exchange,提问作者Michael Sanford
相关产品推荐
相关产品推荐

