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

React使用useReducer提升状态时触发跨组件更新错误的问询

React useReducer状态提升时报错:Cannot update a component while rendering another component

我正在学习useReducer函数,尝试用它构建一个基于React的测验选择界面,目标是将组件内的状态更新后提升至主App组件,供其他模块使用。

遇到的问题

  • 将reducer放在组件外部时,无法完成状态设置;
  • 将reducer放在组件内部时,状态能成功提升到App组件,但控制台会抛出以下错误:
    Cannot update a component (App) while rendering a different component (GetQuestionDATA)
    

GetQuestionDATA组件代码

import { useReducer } from "react";
import React from "react";
import Select from "react-select";

export function GetQuestionDATA({ setAPIParams }) {
  // USE REDUCER to get our various Data from state shown on screen.
  // set initial state
  const initialState = {
    amount: "10",
    category: "",
    difficulty: "",
    type: "",
  };

  // set the Amount, with reducer
  const [state, dispatch] = useReducer(reducer, initialState);

  function reducer(state, action) {
    // we don't tend to use ifs or terniary in reducers instead we look at using the switch keyword, which takes our action type and creates a case for each type.
    switch (action.type) {
      case "setAmount":
        return { ...state, amount: action.payload };
      case "setCategory":
        return { ...state, category: action.payload };
      case "setDifficulty":
        return { ...state, difficulty: action.payload };
      case "setType":
        return { ...state, type: action.payload };
      case "start":
        return setAPIParams(state);

      default:
        throw new Error("Unknown action");
    }
  }

  const defineAmount = function (e) {
    dispatch({ type: "setAmount", payload: Number(e.target.value) });
  };

  const defineCategory = function (e) {
    dispatch({ type: "setCategory", payload: e.value });
  };

  const defineDifficulty = function (e) {
    dispatch({ type: "setDifficulty", payload: e.value });
  };

  const defineType = function (e) {
    dispatch({ type: "setType", payload: e.value });
  };

  const start = function () {
    dispatch({ type: "start" });
  };

  // set state based on choices (dispatch + action)
  // Buncle the state into an object (reducer)
  //----- jsx options -----//
  // category
  const category_options = [
    { value: "9", label: "General Knowledge" },
    { value: "10", label: "Entertainment: Books" },
    { value: "11", label: "Entertainment: Film" },
    { value: "12", label: "Entertainment: Music" },
    { value: "13", label: "Entertainment: Musical & Theaters" },
    { value: "14", label: "Entertainment: Television" },
    { value: "15", label: "Entertainment: Video Games" },
    { value: "16", label: "Entertainment: Board Games" },
    { value: "17", label: "Science & Nature" },
    { value: "18", label: "Science: Computers" },
    { value: "19", label: "Science: Mathmatics" },
    { value: "20", label: "Mythology" },
    { value: "21", label: "Sports" },
    { value: "22", label: "Geography" },
    { value: "23", label: "History" },
    { value: "24", label: "Politics" },
    { value: "25", label: "Art" },
    { value: "26", label: "Celebrities" },
    { value: "27", label: "Animals" },
  ];

  // difficulty
  const difficulty_options = [
    { value: "easy", label: "Easy" },
    { value: "medium", label: "Medium" },
    { value: "hard", label: "Hard" },
  ];

  // type
  const type_options = [
    { value: "multiple", label: "Multiple Choice" },
    { value: "boolean", label: "True / False" },
  ];

  // encoding (hidden)
  return (
    <div className="quiz-selection">
      <p>Number Of Questions</p>
      <div className="quiz-buttons">
        <span>
          <button value="10" onClick={defineAmount}>
            10
          </button>
        </span>
        <span>
          <button value="20" onClick={defineAmount}>
            20
          </button>
        </span>
        <span>
          <button value="30" onClick={defineAmount}>
            30
          </button>
        </span>
        <span>
          <button value="40" onClick={defineAmount}>
            40
          </button>
        </span>
        <span>
          <button value="50" onClick={defineAmount}>
            50
          </button>
        </span>
      </div>
      <div className="select">
        <p className="cat-header">Category</p>
        <Select onChange={defineCategory} options={category_options} />
        <p className="cat-header">Difficulty</p>
        <Select onChange={defineDifficulty} options={difficulty_options} />
        <p className="cat-header">Type</p>
        <Select onChange={defineType} options={type_options} />
      </div>
      <div>
        <button className="quiz-buttons" onClick={start}>
          Start
        </button>
      </div>
    </div>
  );
}

补充说明

我知道Context和Redux可以解决这类状态共享问题,但目前还没学到相关内容,希望用已掌握的useReducer+状态提升的方式修复。

尝试过的方法:

  • 用useEffect包裹相关逻辑,但控制台提示不能在回调中包裹reducer;
  • 将reducer移进/移出组件,结果要么还是出现相同报错(状态能正常提升),要么完全无法通过setState提升状态。

问题解决

编辑:已解决问题。解决方案是在组件内创建一个独立状态,让reducer负责更新这个组件内部状态,然后通过useEffect监听该状态的变化,在回调中调用App组件传递过来的setAPIParams来完成状态提升。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 18:04:55