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

禁用Strict Mode后React组件仍渲染两次及显示undefined问题排查

React组件渲染异常问题解答

问题1:Typography组件短暂显示undefined的原因及解决

原因

MainInput组件中,calculatedNums初始状态设为空对象{}:

const [calculatedNums, setCalculatedNums] = useState({});

组件第一次渲染时,calculatedNums.number1和calculatedNums.number2均为undefined,导致Typography直接渲染该值。后续依赖generateNewNumbers的useEffect执行,调用calculation()生成数值并更新calculatedNums,触发第二次渲染才显示实际内容,因此出现短暂的undefined。

解决方案

方案1:初始化时直接生成数值

将calculatedNums的初始状态改为调用calculation(),让第一次渲染就有可用值:

const [calculatedNums, setCalculatedNums] = useState(calculation());

同时可移除挂载时触发的useEffect(若仅用于初始赋值),保留其用于后续生成新数值的逻辑即可。

方案2:添加条件渲染/可选链

在Typography中使用可选链操作符避免渲染undefined,或添加加载占位符:

<Typography fontSize={28}>
  {operation !== "/"
    ? `${calculatedNums?.number1} ${operation} ${calculatedNums?.number2}`
    : `${calculatedNums?.number2} ${operation} ${calculatedNums?.number1}`}{" "}
  =
</Typography>

或用占位符过渡:

<Typography fontSize={28}>
  {calculatedNums.number1 ? (
    operation !== "/"
      ? `${calculatedNums.number1} ${operation} ${calculatedNums.number2}`
      : `${calculatedNums.number2} ${operation} ${calculatedNums.number1}`
  ) : "Loading..."} {" "}
  =
</Typography>

问题2:组件两次渲染的原因及说明

原因

主要由两个因素触发:

  1. React StrictMode开发环境检测
    在React开发环境下,StrictMode会强制组件渲染两次(包括函数组件执行、useEffect执行两次),目的是检测潜在的副作用问题(如未清理的定时器、重复状态更新)。这是开发环境的正常机制,生产环境会自动关闭。

  2. 状态更新触发重渲染
    MainInput挂载后,依赖generateNewNumbers的useEffect执行setCalculatedNums(calculation()),更新calculatedNums状态,触发第二次渲染。

说明与优化

  • 若为StrictMode导致的两次渲染,无需特殊处理,生产环境会自动消失。
  • 采用问题1的方案1(初始化时直接赋值calculatedNums),可避免挂载时的额外状态更新,减少一次不必要的重渲染。

额外代码优化建议

  1. AdditionMain中每次渲染都会调用generateNumbersAndResults().addition,用useMemo缓存避免重复计算:
const calculation = useMemo(() => generateNumbersAndResults().addition, []);
  1. Beginning组件定时器逻辑简化,无需使用ref,直接依赖count状态:
useEffect(() => {
  const interval = setInterval(() => {
    if (count === 1) {
      setMessage("GO");
    } else {
      setCount(prev => prev - 1);
    }
  }, 1000);

  let timer;
  if (message === 'GO') {
    timer = setTimeout(() => {
      dispatch(start());
    }, 1000);
  }
  
  return () => {
    clearInterval(interval);
    if (timer) clearTimeout(timer);
  };
}, [count, message, dispatch]);

内容的提问来源于stack exchange,提问作者Süleyman Eminbəyli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 04:25:38