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

使用useReducer在其他组件更新状态时组件未重渲染,原因何在?

问题:ComponentA调用dispatch后ComponentB未重渲染的原因及预期性分析

我有三个文件:store.js定义reducer与初始状态,ComponentA.js负责分发状态变更,ComponentB.js监听状态变更并渲染状态。但在ComponentA中调用dispatch后,ComponentB并未重渲染,这是为什么?该行为是否符合预期?

相关代码

// store.js
import { useReducer } from "react";

const initialState = {
  firstName: "",
  lastName: "",
};

function reducer(state, { fieldName, fieldValue }) {
  return {
    ...state,
    [fieldName]: fieldValue,
  };
}

export function useForm() {
  return useReducer(reducer, initialState);
}
// ComponentA.js
import { useForm } from "./store";

export function ComponentA() {
  const [{ firstName }, dispatch] = useForm();

  return (
    <input
      type="text"
      value={firstName}
      onChange={(e) => {
        dispatch({
          fieldName: "firstName",
          fieldValue: e.target.value,
        });
      }}
    />
  );
}
// ComponentB.js
import { useForm } from "./store";

export function ComponentB() {
  const [{ firstName }] = useForm();

  return <p>{firstName}</p>;
}
// App.js
import { ComponentA } from "./ComponentA";
import { ComponentB } from "./ComponentB";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <ComponentA />
      <ComponentB />
    </div>
  );
}

原因分析

核心问题在于**useForm每次调用都会创建独立的useReducer实例**:

  • ComponentA和ComponentB各自导入并调用useForm(),两个组件拥有完全独立的状态副本,彼此没有关联。
  • 当ComponentA调用dispatch时,只会更新自己内部的状态,ComponentB的状态仍然是初始的{firstName: ""},因此不会触发重渲染。

是否符合预期

该行为完全符合React Hook的设计预期:

  • useReducer(及所有React状态Hook)的核心特性是组件级状态隔离,每个组件调用Hook时都会创建独立的状态上下文,默认不会在组件间共享状态。如果需要共享状态,需手动通过状态提升、Context API或状态管理库(如Redux)实现。

解决方案:用Context共享状态

借助React Context API,可将useReducer的状态和dispatch函数共享给子组件,实现状态同步:

修改后的store.js

import { useReducer, createContext, useContext } from "react";

const initialState = {
  firstName: "",
  lastName: "",
};

function reducer(state, { fieldName, fieldValue }) {
  return {
    ...state,
    [fieldName]: fieldValue,
  };
}

const FormContext = createContext();

export function FormProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <FormContext.Provider value={{ state, dispatch }}>
      {children}
    </FormContext.Provider>
  );
}

export function useForm() {
  return useContext(FormContext);
}

修改后的App.js

import { ComponentA } from "./ComponentA";
import { ComponentB } from "./ComponentB";
import { FormProvider } from "./store";
import "./styles.css";

export default function App() {
  return (
    <FormProvider>
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
        <ComponentA />
        <ComponentB />
      </div>
    </FormProvider>
  );
}

修改后的ComponentA.js

import { useForm } from "./store";

export function ComponentA() {
  const { state: { firstName }, dispatch } = useForm();

  return (
    <input
      type="text"
      value={firstName}
      onChange={(e) => {
        dispatch({
          fieldName: "firstName",
          fieldValue: e.target.value,
        });
      }}
    />
  );
}

修改后的ComponentB.js

import { useForm } from "./store";

export function ComponentB() {
  const { state: { firstName } } = useForm();

  return <p>{firstName}</p>;
}

修改后,ComponentA和ComponentB共享同一个状态实例,调用dispatch时会同步更新所有消费该Context的组件,ComponentB即可正确重渲染。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 00:40:20