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

React中useReducer内调用Hook报错的解决方案问询

问题根源

你遇到的报错完全是因为违反了React Hook的调用规则:Hook只能在组件顶层或自定义Hook内部调用,绝对不能在reducer、useEffect回调、循环/判断逻辑里使用。你的typeOfDevReducer是纯状态计算函数,里面调用useAppDispatch直接违反了这条规则,导致Hook调用顺序混乱,触发React的报错。

解决方案

核心思路是把reducer还原成纯函数(只负责根据action计算新状态,不处理任何副作用),将原本在reducer里的Redux dispatch逻辑移到组件层面处理。

步骤1:修改reducer,移除Hook调用

将reducer改成纯函数,只保留状态计算逻辑:

export default (state: IInitState, action: reducerAction): IInitState => {
  switch (action.type) {
    case REDUCER_ACTION_TYPE.GET_USER: {
      console.log(action.payload);
      return { ...state, typeOfDev: { ...action.payload!.value } };
    }
    case REDUCER_ACTION_TYPE.SET_FIRST_TIME: {
      return { ...state, firstTime: true };
    }
    // 其他action的处理逻辑...
    default:
      return state;
  }
};

步骤2:在组件中处理Redux dispatch逻辑

有两种常用方式实现,根据你的场景选择:

方式一:每次调用useReducer的dispatch后手动触发Redux dispatch

在组件顶层先正常调用useAppDispatch(符合Hook规则),然后在请求回调里调用完useReducer的dispatch后,同步触发Redux的action:

const [{ firstTime, typeOfDev, technologies }, dispatch] = useReducer(
  typeOfDevReducer,
  initialState
);
// 在组件顶层调用Hook,符合规则
const dispatchRedux = useAppDispatch();

useEffect(() => {
  const getTypeOfDev = async () => {
    try {
      const typeOfDev = (await axiosPrivate.get<ITypeOfDev>(`type-of-dev/get/${user!.id}`))
        .data;
      dispatch({
        type: REDUCER_ACTION_TYPE.GET_USER,
        payload: { value: typeOfDev },
      });
      // 触发Redux action
      dispatchRedux(setObjectValueChanged(true));
    } catch (err: any) {
      if (err.response?.status === 404) {
        dispatch({ type: REDUCER_ACTION_TYPE.SET_FIRST_TIME });
        // 触发Redux action
        dispatchRedux(setObjectValueChanged(true));
      }
    } finally {
      dispatchRedux(setObjectValueChanged(false));
    }
  };

  getTypeOfDev();
}, [axiosPrivate, user, dispatch, dispatchRedux]);

方式二:监听useReducer状态变化自动触发Redux dispatch

如果多个action都需要触发同一个Redux action,可以用useEffect监听useReducer的状态,一旦状态更新就自动触发:

const [{ firstTime, typeOfDev, technologies }, dispatch] = useReducer(
  typeOfDevReducer,
  initialState
);
const dispatchRedux = useAppDispatch();

// 监听状态变化,自动触发Redux action
useEffect(() => {
  dispatchRedux(setObjectValueChanged(true));
}, [firstTime, typeOfDev, technologies, dispatchRedux]);

useEffect(() => {
  const getTypeOfDev = async () => {
    try {
      const typeOfDev = (await axiosPrivate.get<ITypeOfDev>(`type-of-dev/get/${user!.id}`))
        .data;
      dispatch({
        type: REDUCER_ACTION_TYPE.GET_USER,
        payload: { value: typeOfDev },
      });
    } catch (err: any) {
      if (err.response?.status === 404) {
        dispatch({ type: REDUCER_ACTION_TYPE.SET_FIRST_TIME });
      }
    } finally {
      dispatchRedux(setObjectValueChanged(false));
    }
  };

  getTypeOfDev();
}, [axiosPrivate, user, dispatch, dispatchRedux]);
关键注意事项
  • reducer必须是纯函数:不能有副作用(比如调用dispatch、发起请求、修改全局变量),只能接收当前状态和action,返回新状态。
  • Hook调用必须在组件顶层:所有Hook(包括useAppDispatch、useReducer等)都要放在组件的最外层,不能嵌套在任何回调、循环、判断里。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 03:13:23