You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React通过context传递的函数访问state始终返回初始值如何解决

问题根因

这是典型的React闭包陷阱问题:
你的Provider组件中,用于往context注入函数的useEffect依赖是空数组,仅会在组件首次挂载时执行1次。此时存入context的addStrings、logStrings函数创建时,闭包捕获的textArray就是初始的空数组。后续textArray更新时,你没有更新context中存储的函数,所以Consumer调用的永远是首次挂载时生成的旧函数,旧函数闭包里的textArray永远是初始值,自然只能打印出空数组。
你看到DevTools中state正常,是因为首次调用addStrings时,基于初始空数组拼接新值确实能完成一次状态更新,但如果多次调用添加方法,你会发现新值会覆盖旧值而不是追加,本质也是同一个闭包问题导致的。

解决方案

这里提供3种可行的修复方案,按合理程度排序:

方案1:调整Context架构(最推荐)

你当前的设计是把状态存在子组件,再把方法回传父组件注入Context,属于完全没必要的多层传递。直接把textArray状态和操作方法都放到Context的Provider(也就是你的TestContainer组件)中,直接注入Context即可,从根源规避闭包问题:

// TestContainer.tsx 调整后代码
import React, { useState, createContext } from "react";
import Consumer from "./Consumer";

export const context = createContext<AppContextType | undefined>(undefined);

type AppContextType = {
  textArray: string[];
  actions: {
    addStrings: (t: string[]) => void;
    logStrings: () => void;
  };
};

const TestContainer = () => {
  const [textArray, setTextArray] = useState<string[]>([]);

  const addStrings = (texts: string[]) => {
    console.log("PREVIOUS LINES: ", textArray);
    console.log("NEW LINES:", [...textArray, ...texts]);
    setTextArray([...textArray, ...texts]);
  }

  const logStrings = () => {
    console.log("Strings : ", textArray);
  }

  return (
    <context.Provider
      value={{
        textArray,
        actions: { addStrings, logStrings }
      }}
    >
      {/* 原Provider组件的渲染逻辑直接移到这里,或者把textArray传给原组件渲染 */}
      <>
        <h1>Strings : </h1>
        {textArray.map((line, index) => (
          <p key={index}>{line}</p>
        ))}
      </>
      <Consumer />
    </context.Provider>
  );
};

export default TestContainer;

方案2:更新Context中存储的函数

如果要保留现有架构,需要在textArray更新时,同步更新Context中的方法:

  1. 用useCallback包裹两个方法,依赖设置为textArray,保证textArray变化时生成新的函数
  2. 给注入方法的useEffect添加这两个方法为依赖,方法更新时同步到Context
// Provider.tsx 调整后核心代码
import React, { useEffect, useState, useCallback } from "react";

// ... 其余代码不变
const Provider = ({ setActions }: ProviderProps) => {
  const [textArray, setTextArray] = useState<string[]>([]);

  // 用useCallback包裹,依赖textArray
  const addStrings = useCallback((texts: string[]) => {
    console.log("PREVIOUS LINES: ", textArray);
    console.log("NEW LINES:", [...textArray, ...texts]);
    setTextArray([...textArray, ...texts]);
  }, [textArray]);

  const logStrings = useCallback(() => {
    console.log("Strings : ", textArray);
  }, [textArray]);

  // 依赖添加两个方法,变化时同步更新Context
  useEffect(() => {
    console.log("send functions to parent");
    setActions({ addStrings, logStrings });
  }, [addStrings, logStrings, setActions]);

// ... 其余代码不变

方案3:用useRef存储最新状态

如果你不想频繁更新Context中的函数,可以用ref同步最新的textArray值,函数中直接读取ref即可:

// Provider.tsx 调整后核心代码
const Provider = ({ setActions }: ProviderProps) => {
  const [textArray, setTextArray] = useState<string[]>([]);
  // 创建ref同步最新状态
  const textArrayRef = useRef(textArray);
  // 状态变化时更新ref
  useEffect(() => {
    textArrayRef.current = textArray;
  }, [textArray]);

  function addStrings(texts: string[]) {
    // 读ref拿最新值
    console.log("PREVIOUS LINES: ", textArrayRef.current);
    console.log("NEW LINES:", [...textArrayRef.current, ...texts]);
    setTextArray([...textArrayRef.current, ...texts]);
  }

  function logStrings() {
    // 读ref拿最新值
    console.log("Strings : ", textArrayRef.current);
  }

  useEffect(() => {
    setActions({ addStrings: addStrings, logStrings: logStrings });
  }, []);
// ... 其余代码不变

内容的提问来源于stack exchange,提问作者Kian Soltani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 18:00:04