React:子组件回调获取父组件history状态不完整问题求助
问题:React回调函数中获取的history状态不完整
我在回调函数onRestore里打印history时,发现只显示到所点击快照ID为止的历史记录,为什么回调里拿到的状态不对?
相关代码
Game组件:
import React, { useState, useEffect, useRef } from "react" import Board from "./Board" import Snapshot from "./Snapshot" export default function Game(){ const [ history, setHistory ] = useState([makeSnapshot(0, EMPTY_BOARD, true, onRestore)]) const [ offset, setOffset ] = useState(0) function makeSnapshot(index, board, currentPlayer, onRestore){ const snapshot = { index, board, currentPlayer, onRestore } return <Snapshot key={index} {...snapshot}/> } function onRestore(snapshot){ console.log(snapshot) // #2 snapshot item console.log(history) // log only 2 snapshot items (if snapshot item id is #3 so 3 items in history) } function drawPlayer(index){ const nextMove = makeSnapshot(history.length, newBoard, currentPlayer, onRestore) setHistory(prevHistory => ([...prevHistory, nextMove])) setOffset(history.length + 1) } return( <div className="Game"> <h1>History: { history.map(snapshot => snapshot) } </h1> </div> ) }
Snapshot组件:
export default function Snapshot(snapshot){ const move = !snapshot.index ? `game start` : `move #${snapshot.index}` const disableClass = snapshot.isDisabled ? " disabled" : "" return( <div id={snapshot.index} className="snapshot" onClick={() => snapshot.onRestore(snapshot)}> Go to {move} </div> ) }
实时日志截图

原因分析
这是React中闭包捕获状态的特性导致的:
你创建每个Snapshot组件时,传入的onRestore函数会捕获创建该快照时的history状态。比如创建第2个快照时,onRestore里的history就是包含前2个快照的数组;后续添加更多快照后,旧的onRestore函数依然持有它被创建时的history引用,不会自动更新到最新的完整状态。
解决方案
有两种实用的解决方式:
方式1:用useRef保存最新的history引用
把history的实时状态同步到ref中,这样回调里总能拿到最新的完整历史:
export default function Game(){ const [ history, setHistory ] = useState([makeSnapshot(0, EMPTY_BOARD, true, onRestore)]) const historyRef = useRef(history) // 同步history状态到ref useEffect(() => { historyRef.current = history }, [history]) function onRestore(snapshot){ console.log(snapshot) console.log(historyRef.current) // 这里拿到最新的完整history } // 其他代码保持不变 }
方式2:函数式更新获取最新状态(若需修改history)
如果你的onRestore需要修改history状态,直接在setHistory里用函数式更新,参数prevHistory就是当前最新的完整状态:
function onRestore(snapshot){ setHistory(prevHistory => { console.log(prevHistory) // prevHistory是最新的完整历史 // 示例:回退到当前快照对应的历史节点 return prevHistory.slice(0, snapshot.index + 1) }) }
内容的提问来源于stack exchange,提问作者Osher
相关产品推荐
相关产品推荐

