手写React状态hook


在日常开发中,我们经常用到 React 的状态管理 Hook:useStateuseReducer

但你有没有想过:这些 Hook 内部是怎么实现的?为什么调用 setState 之后组件会重新渲染?

今天我们就来从零手写 useState 和 useReducer,理解它们的原理。


一、React Hook 的本质

在 React 中,每个函数组件调用时都会按照 调用顺序 来记录 Hook。

比如下面的例子:

jsx 复制代码
function Counter() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("张三");

  return (
    <div>
      <p>{count}</p>
      <p>{name}</p>
    </div>
  );
}

React 内部会维护一个 数组(或链表) 来存储这些 Hook 对应的状态:

复制代码
hooks = [
  { state: 0 },        // count
  { state: "张三" }     // name
]
  • 每次组件渲染时,useState 都会返回这个位置对应的状态。
  • 调用 setState 时,会更新状态并触发组件重新渲染。

所以:Hook 的核心 = 存储状态 + 按顺序取出


二、手写 useState

我们先写一个最简版的 React Hook 运行环境:

js 复制代码
let hookStates = []; // 存放所有 hook 的状态
let hookIndex = 0;   // 当前执行到第几个 hook

function useState(initialValue) {
  const currentIndex = hookIndex; // 记录当前 hook 的位置

  // 第一次渲染:存储初始值
  if (hookStates[currentIndex] === undefined) {
    hookStates[currentIndex] = initialValue;
  }

  // 获取当前状态
  const state = hookStates[currentIndex];

  // 更新函数
  const setState = (newValue) => {
    hookStates[currentIndex] =
      typeof newValue === "function"
        ? newValue(hookStates[currentIndex])
        : newValue;
    render(); // 触发重新渲染
  };

  hookIndex++; // 下一个 hook 位置
  return [state, setState];
}

这里我们做了几件事:

  1. hookStates 保存所有状态。
  2. hookIndex 保证按顺序存取。
  3. setState 更新状态后调用 render,模拟 React 重新渲染。

三、测试 useState

写一个小 demo:

js 复制代码
function Counter() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("张三");

  console.log("render:", { count, name });

  return {
    add: () => setCount(count + 1),
    rename: () => setName("李四"),
  };
}

// 模拟渲染
function render() {
  hookIndex = 0; // 每次渲染重置
  app = Counter();
}

let app;
render();

// 测试
app.add();    // count 从 0 → 1
app.rename(); // name 从 "张三" → "李四"

运行过程:

复制代码
render: { count: 0, name: '张三' }
render: { count: 1, name: '张三' }
render: { count: 1, name: '李四' }

✅ 我们的 useState 成功模拟了 React 的状态管理!


四、手写 useReducer

useReducer 的本质就是 useState 的加强版:

  • useState 直接存值
  • useReducerreducer 函数 来更新值

我们来实现它:

js 复制代码
function useReducer(reducer, initialValue) {
  const [state, setState] = useState(initialValue);

  function dispatch(action) {
    const newState = reducer(state, action);
    setState(newState);
  }

  return [state, dispatch];
}

是不是很眼熟?

其实 React 源码里 useState 就是 useReducer 的语法糖:

js 复制代码
function useState(initialValue) {
  return useReducer((state, action) => action, initialValue);
}

五、测试 useReducer

来写一个计数器:

js 复制代码
function reducer(state, action) {
  switch (action.type) {
    case "add":
      return state + 1;
    case "sub":
      return state - 1;
    default:
      return state;
  }
}

function Counter2() {
  const [count, dispatch] = useReducer(reducer, 0);

  console.log("render:", { count });

  return {
    add: () => dispatch({ type: "add" }),
    sub: () => dispatch({ type: "sub" }),
  };
}

// 模拟渲染
function render() {
  hookIndex = 0;
  app = Counter2();
}

let app;
render();

app.add(); // count: 0 → 1
app.add(); // count: 1 → 2
app.sub(); // count: 2 → 1

输出:

复制代码
render: { count: 0 }
render: { count: 1 }
render: { count: 2 }
render: { count: 1 }

完美模拟 ✅


六、总结

  • useState :存储值,返回 [state, setState]
  • useReducer :存储值 + reducer 逻辑,返回 [state, dispatch]
  • 关系useState = useReducer 的简化版。

什么时候用哪个?

  • 逻辑简单(计数器、表单输入) → 用 useState
  • 逻辑复杂(多个状态、复杂操作) → 用 useReducer

结语

通过手写,我们发现 React Hook 并没有什么黑魔法:

它只是 按照调用顺序存储状态,并在更新时触发重新渲染。

理解了原理,再写业务代码时就更清晰:

为什么 Hook 不能写在 if/for 里?为什么每次渲染必须顺序一致?

------ 因为 React 就是用数组来按顺序存储的

相关推荐
前端摸鱼匠32 分钟前
Vue 3 的v-bind合并行为:讲解v-bind与普通属性合并的规则
前端·javascript·vue.js·前端框架·ecmascript
REDcker1 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
donecoding2 小时前
一个 sudo 引发的血案:npm 全局包权限错乱彻底修复
前端·node.js·前端工程化
风骏时光牛马2 小时前
Raku正则匹配与数据批量处理实操案例
前端
nbwenren3 小时前
2026实测:Gemini 3 镜像站视觉能力实践——拍照原型图,一键生成 HTML+CSS 代码
前端·css·html
Lee川3 小时前
Prisma 实战指南:像搭积木一样设计古诗词数据库
前端·数据库·后端
Linsk3 小时前
Java和JavaScript的关系真是雷峰和雷峰塔的关系吗?
java·javascript·oracle
当时只道寻常3 小时前
浏览器文本复制到剪贴板:企业级最佳实践
javascript
jinanwuhuaguo3 小时前
(第二十九篇)OpenClaw 实时与具身的跃迁——从异步孤岛到数字世界的“原住民”
前端·网络·人工智能·重构·openclaw