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

如何正确拆分dispatch与state避免React组件不必要重渲染?

问题与解决方案

问题现象

使用useContext + useReducer实现状态管理时,触发其中一个组件的dispatch会导致所有关联组件(如增减按钮)都触发重渲染,即使组件仅依赖dispatch而未使用state。

问题原因

虽然拆分了ApplicationContextState和ApplicationContextDispatch两个Context,但每次MyContextProvider重渲染时,都会创建新的{ dispatch }和{ state }对象,导致依赖这两个Context的组件检测到value引用变化,从而触发重渲染。

解决方案

1. 稳定Context Value的引用

对dispatch的Context值使用useMemo包裹,因为useReducer返回的dispatch函数本身是稳定的(不会随渲染变化),这样能确保dispatch的Context value引用始终不变:

修改后的Provider代码:

import * as React from 'react';
import {
  InitalState,
  ApplicationContextDispatch,
  ApplicationContextState,
} from './Context';
import { applicationReducer } from './Reducer';

export const MyContextProvider = ({ children }) => {
  const [state, dispatch] = React.useReducer(applicationReducer, InitalState);
  
  // 用useMemo稳定dispatch的value引用
  const dispatchValue = React.useMemo(() => ({ dispatch }), [dispatch]);
  // 对state的value也做稳定处理(可选,但更规范)
  const stateValue = React.useMemo(() => ({ state }), [state]);

  return (
    <ApplicationContextDispatch.Provider value={dispatchValue}>
      <ApplicationContextState.Provider value={stateValue}>
        {children}
      </ApplicationContextState.Provider>
    </ApplicationContextDispatch.Provider>
  );
};

2. 用React.memo包裹仅依赖dispatch的组件

对于只使用dispatch、不依赖state的组件(如增减按钮),用React.memo包裹,确保组件仅在props变化时重渲染:

示例按钮组件:

import { useApplicationContextDispatch } from './Context';
import { ActionTypes } from './Context';
import React from 'react';

export const ButtonIncrement = React.memo(() => {
  const { dispatch } = useApplicationContextDispatch();
  console.log('Increment button rendered');
  return <button onClick={() => dispatch({ type: ActionTypes.increment })}>+</button>;
});

export const ButtonDecrement = React.memo(() => {
  const { dispatch } = useApplicationContextDispatch();
  console.log('Decrement button rendered');
  return <button onClick={() => dispatch({ type: ActionTypes.decrement })}>-</button>;
});

原始代码参考

App组件

export default function App() {
  return (
    <MyContextProvider>
      <Count />
      <ButtonIncrement /> <br /> <br />
      <ButtonDecrement />
    </MyContextProvider>
  );
}

Context定义

import React, { Dispatch } from 'react';

export enum ApplicationActions {
  increment = 'increment',
  decrement = 'decrement',
  notification = 'Notification',
}

export type ActionType = ApplicationActions;

export const ActionTypes = { ...ApplicationActions };

export type StateType = {
  count: number;
  notification: string | undefined;
};

export type Action = {
  type: ActionType;
  payload?: string | undefined;
};

interface IContextPropsState {
  state: StateType;
}

interface IContextPropsDispatch {
  dispatch: Dispatch<Action>;
}

export const ApplicationContextState = React.createContext<IContextPropsState>(
  {} as IContextPropsState
);

export const ApplicationContextDispatch =
  React.createContext<IContextPropsDispatch>({} as IContextPropsDispatch);

export const useApplicationContextState = (): IContextPropsState => {
  return React.useContext(ApplicationContextState);
};

export const useApplicationContextDispatch = (): IContextPropsDispatch => {
  return React.useContext(ApplicationContextDispatch);
};

export const InitalState: StateType = {
  count: 0,
  notification: '',
};

Reducer定义

import { StateType, Action, ActionTypes } from './Context';

export const applicationReducer = (
  state: StateType,
  action: Action
): StateType => {
  const { type } = action;
  switch (type) {
    case ActionTypes.increment:
      return { ...state, count: state.count + 1 };
    case ActionTypes.decrement:
      return { ...state, count: state.count - 1 };
    case ActionTypes.notification:
      return { ...state, notification: action.payload };
    default:
      return state;
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 20:54:26