唉,我是专门来写 bug 的吧

一句话总结: StrictMode 下,不要在 useState、useRef 等初始值的构造函数/回调函数里修改 state

起因是,我需要写这样一个代码

  1. 有一个通过单例模式创建的对象;
  2. 这个单例对象初始化完成以后,需要刷新页面状态;

正常的前端,大概会把对象初始化的逻辑写在 useEffect 或者 useEffectLayout

可是,今天的我脑洞清奇,因为我想让早点初始化完成,所以我把对象的初始化写在了 useState 的初始值里,这样执行的时机会在 useEffect 或者 useEffectLayout 之前

js 复制代码
import React, { useState } from 'react';

class Onething {
  constructor(props) {
    console.log('*******constructor');
    props.init();
  }

  static instance;

  static getInstance(props) {
    console.log('*******getInstance');
    if (!this.instance) {
      this.instance = new Onething(props);
    }
    return this.instance;
  }
}

const Example = () => {
  const init = () => {
    setText('完成');
  }
  const [text, setText] = useState('初始');
  const [thing] = useState(Onething.getInstance({ init }));

  console.log('******render', text);

  return <div>{text}</div>;
}

我心里,这个代码的执行顺序是这样的:

  1. 将 text 初始化为 "初始";
  2. 执行 Onething 的构造函数,将 text 修改为 "完成";

会先初始化 text 为 "初始",再执行 Onething 的构造函数,构造函数里将 text 变成 "完成",所以最后界面会展示"完成"。

结果!这个代码!!没有照我的预期运行!!!单例对象初始化完成后,界面依然显示"初始"!!!!

打开控制台,组件函数在 StrictMode 下执行了两遍,

  1. 将 text 初始化为 "初始";
  2. 执行 Onething 的构造函数,将 text 修改为 "完成";
  3. StrictMode 下第二次执行组件函数,再次将 text 初始化为 "初始";

由于 Onething 是单例模式,第二次执行时没有执行构造函数、再修改 text 值,所以 text 值又变成了 "初始";

唉,唉,唉

我是专门来写 bug 的吧

相关推荐
zzzzzz3107 小时前
react-bits:从 36K Stars 的组件库,看动画交互组件该如何被评估
前端·react.js·动效
旋生万物19 小时前
【终极实战】用Python从零“生成“一个宇宙:螺旋干涉模型的代码实现
开发语言·前端·人工智能·react.js·php·wpf
半个落月21 小时前
在浏览器里运行 DeepSeek-R1:React 对话界面与安全渲染(三)
前端·人工智能·react.js
小林ixn1 天前
React + Zustand + JWT:从零实现登录鉴权与请求拦截
前端·react.js·前端框架
光影少年1 天前
react native打包优化、包体积瘦身、离线包/热更新方案
前端·react native·react.js
半个落月1 天前
在浏览器里运行 DeepSeek-R1:推理、流式输出与停止生成(二)
前端·人工智能·react.js
半个落月1 天前
在浏览器里运行 DeepSeek-R1:从 WebGPU 检测到模型加载(一)
前端·人工智能·react.js
风月说与山鬼1 天前
六、React事件
前端·javascript·react.js
YHHLAI1 天前
实战 Vibe Coding:用 AI 从零搭一个 React 待办清单
前端·人工智能·react.js
zzzzzz3101 天前
别急着把动效组件搬进页面:从 react-bits 看 React 动效库该怎么评估
前端·react.js·开源