如何在函数内部访问React Hooks的state状态?
问题根源:React Hooks闭包陷阱
你遇到的核心问题是闭包导致的旧状态捕获:questionComponent函数在组件每次渲染时被定义,它会捕获当前渲染周期的appState引用。当你调用setAppState更新状态后,组件会重新渲染,但之前通过questionComponent生成并存在currentCard里的组件,依然持有旧的appState值——因为那个函数是在旧渲染周期创建的,闭包保留了当时的状态。
另外你代码里还有一个小错误:addToQuestion中questionLength.current = appState.questions.length是错的,应该用回调参数prevState获取当前长度,因为setAppState回调里的appState是旧状态。
解决方案(推荐方案:遵循React数据流)
不要把组件实例存在currentCard状态里,改成存储卡片的标识(比如currentCardKey),然后在渲染阶段根据标识生成组件。这样每次渲染都会自动获取最新的appState,彻底避免闭包问题。
修改后的核心代码:
function App(){ const [appState, setAppState] = useState({ questions: [], independentVariablesCount: 0, }) // 存储卡片key,而非组件实例 const [currentCardKey, setCurrentCardKey] = useState(); const addToQuestion = (questionObject) => { setAppState(prevState => { // 用prevState获取当前长度,而非appState // questionLength.current = prevState.questions.length return {...prevState, questions: [...prevState.questions, questionObject]} }) } // 直接在渲染时根据key生成组件,自动拿到最新appState const renderCurrentCard = () => { if (!currentCardKey) return null; return <Question textAreaText={"ereer"} shouldShowBackButton={appState.questions.length > 0} measureTextChangeHandler={measureTextChangeHandler} onChosenDataTypeChanged={onChosenDataTypeChanged} aimTextChangeHandler={aimTextChangeHandler} onButtonClicked={onButtonClicked} currentCardKey={currentCardKey}/> } const onButtonClicked = (_buttonText, previousCardComponent) => { const previousCardMainText = previousCardComponent.props.mainText switch (_buttonText) { case buttonText["NEXT"]: switch (previousCardMainText){ case questionData["CAT_QUESTION"]: addToQuestion(previousCardComponent) // 设置卡片key,而非组件 setCurrentCardKey("CAT_FOOD_QUESTION") break } break } } return ( <div>{renderCurrentCard()}</div> ) }
备选方案(若必须存组件到state)
如果业务场景确实需要把组件存在state里,可以用useRef保存最新的appState引用,让questionComponent总能拿到最新状态:
function App(){ const [appState, setAppState] = useState({ questions: [], independentVariablesCount: 0, }) const [currentCard, setCurrentCard] = useState(); // 用ref保存最新状态 const appStateRef = useRef(appState); // 状态更新时同步ref useEffect(() => { appStateRef.current = appState; }, [appState]) const addToQuestion = (questionObject) => { setAppState(prevState => { return {...prevState, questions: [...prevState.questions, questionObject]} }) } let questionComponent = (currentCardKey, textAreaText = "") => { // 从ref获取最新状态 const latestAppState = appStateRef.current; console.log(`Question length inside: ${latestAppState.questions.length}`) return <Question textAreaText={"ereer"} shouldShowBackButton={latestAppState.questions.length > 0} measureTextChangeHandler={measureTextChangeHandler} onChosenDataTypeChanged={onChosenDataTypeChanged} aimTextChangeHandler={aimTextChangeHandler} onButtonClicked={onButtonClicked} currentCardKey={currentCardKey}/> } // 其余代码不变 }
内容的提问来源于stack exchange,提问作者FlashspeedIfe
相关产品推荐
相关产品推荐

