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

更新React Context值时useEffect钩子为何不触发?

问题描述

在分步向导中使用React Context收集用户数据,最终在最后一步处理这些数据。Person字段可正常更新且后续页面能获取,但输入姓名和地址后,尝试通过useEffect调用API做处理时,useEffect钩子完全不触发。尝试依赖personState或其属性的写法均无效。

原代码示例

export interface Person {
  name: string;
  address: string;
  skills: string[]; // 在向导的其他页面收集
};

const PersonContext = React.createContext(null);

const PersonProvider: React.FC = ({children}) => {
  const [personState, setPersonState] = useState<Person>({
    name: "",
    address: ""
  });

  return ( 
    <PersonContext.Provider value={personState}>
      {children}
    </PersonContext.Provider>
  );
};

export const PersonPage: React.FC<{}> = () => {
  const personState = useContext(PersonContext);
  
  useEffect(() => {
    console.log("useEffect");
  }, [personState]);
  
  /*
  也试过这种写法,同样无效:

  useEffect(() => {
    console.log("useEffect");
  }, [personState.name, personState.address]);
  */

  const onNameChanged = (e) => {
    if(event.key === "Enter") {
      console.log(`name: ${e.target.value}`);
      personState.name = e.target.value;
    }
  };
  
  const onAddressChanged = (e) => {
    if(event.key === "Enter") {
      console.log(`name: ${e.target.value}`);
      personState.address = e.target.value;
    }
  };
  
  return (
    <div>
      Name: <input name="name" onKeyDown={(e) => onNameChanged(e)} /> (按回车确认)
      <br />
      你输入的是: <span>{personState.name}</span>
      <br/>
      <br/>
      Address: <input name="address" onKeyDown={(e) => onAddressChanged(e)} /> (按回车确认)
      <br />
      你输入的是: <span>{personState.address}</span>
    </div>
  );
};

export const App: React.FC<{}> = () => {
  return (
    <PersonProvider>
      <PersonPage />
    </PersonProvider>
  );
}

问题原因

核心问题是直接修改了state对象的属性,没有使用React提供的setPersonState更新函数。React的状态更新依赖不可变原则,直接修改对象引用不会触发组件重新渲染,useEffect的依赖对比也不会检测到变化,因此钩子不会执行。另外原代码存在事件变量未定义的错误:事件处理函数中使用了未声明的event,应该用参数e代替。

修复方案

  1. 在PersonProvider中,将setPersonState加入Context的value中,让子组件可以调用状态更新方法。
  2. 修改事件处理函数,通过setPersonState以不可变的方式更新状态。
  3. 修正事件参数的错误。

修改后的完整代码

export interface Person {
  name: string;
  address: string;
  skills: string[]; // 在向导的其他页面收集
};

// 更新Context类型,包含state和更新方法
const PersonContext = React.createContext<{
  personState: Person;
  setPersonState: React.Dispatch<React.SetStateAction<Person>>;
} | null>(null);

const PersonProvider: React.FC = ({children}) => {
  const [personState, setPersonState] = useState<Person>({
    name: "",
    address: "",
    skills: []
  });

  return ( 
    <PersonContext.Provider value={{ personState, setPersonState }}>
      {children}
    </PersonContext.Provider>
  );
};

export const PersonPage: React.FC<{}> = () => {
  const { personState, setPersonState } = useContext(PersonContext) || {};
  
  useEffect(() => {
    console.log("useEffect触发了");
    // 这里可以调用API处理数据
  }, [personState.name, personState.address]);

  const onNameChanged = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if(e.key === "Enter") {
      const value = e.currentTarget.value;
      console.log(`name: ${value}`);
      // 不可变更新state
      setPersonState(prev => ({ ...prev, name: value }));
    }
  };
  
  const onAddressChanged = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if(e.key === "Enter") {
      const value = e.currentTarget.value;
      console.log(`address: ${value}`);
      // 不可变更新state
      setPersonState(prev => ({ ...prev, address: value }));
    }
  };
  
  if (!personState || !setPersonState) return null;
  
  return (
    <div>
      Name: <input name="name" onKeyDown={onNameChanged} /> (按回车确认)
      <br />
      你输入的是: <span>{personState.name}</span>
      <br/>
      <br/>
      Address: <input name="address" onKeyDown={onAddressChanged} /> (按回车确认)
      <br />
      你输入的是: <span>{personState.address}</span>
    </div>
  );
};

export const App: React.FC<{}> = () => {
  return (
    <PersonProvider>
      <PersonPage />
    </PersonProvider>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 13:10:28