React - useState 进阶

语法:

scss 复制代码
const [state, setState] = useState(initialState)

参数:

  • setState 它有两种写法

    scss 复制代码
    setState(value)
    ​
    setState(pre => pre + 1)

下面来详细了解一下它为什么会有两种写法。

setState(value)

先看一个例子:

javascript 复制代码
const App = () => {
  const [count, setCount] = useState(0)
  
  const handleClick = () => {
    setCount(count + 1)
    setCount(count + 1)
    setCount(count + 1)
  }
  return <div>
    <h1>{count}</h1>
    <div onClick={handleClick}>点击</div>  
  </div>
}

当点击按钮时,最终页面上展示的会是:1

为什么呢?

  1. state的闭包陷阱(Stale Closure)

    handleClick 函数作用域内的count值,永远是当前渲染时的值,初始值 0

  2. 异步和批处理机制(Async & Batching):

    React 会将 同一个事件处理器 中的所有 setCount 调用批量处理,而不是立即更新

  3. 计算过程:

    • 当前渲染周期中 count = 0
    • 第一次 setCount(count + 1):计划将 count 更新为 1 + 0 = 1
    • 第二次 setCount(count + 1):此时的 count 仍然是 1(不是更新后的 2),所以还是计划更新为 1 + 0 = 1
    • 第三次同理,仍然是计划更新为 1
    • React 合并所有更新,最后只执行一次更新:setCount(count + 1)

因此引入了 函数式写法 setState(pre => pre + 1)

setState(pre => pre + 1)

还是上面的例子:

javascript 复制代码
const App = () => {
  const [count, setCount] = useState(0)
  
  const handleClick = () => {
    setCount(prev => prev + 1)
    setCount(prev => prev + 1)
    setCount(prev => prev + 1)
  }
  return <div>
    <h1>{count}</h1>
    <div onClick={handleClick}>点击</div>  
  </div>
}

此时,当点击按钮时,最终页面上展示的会是:3

为什么呢?我们来看一下执行过程

  • 点击
  • 出发函数 handleClick
  • 3个更新函数依次进入队列:setCount(prev => prev + 1) 入队
  • fiber架构整个渲染过程,render阶段执行计算
  • 开始处理 更新队列 (初始值 state = 0)
  • 执行函数一:0 + 1 = 1
  • 执行函数二:此时pre = 1,故 1 + 1 = 2
  • 执行函数三:此时pre = 2,故 2 + 1 = 3
  • 最终 count = 3
  • Commit 阶段:count = 3 被用于渲染,更新到DOM

函数式更新的优势:

  • 不受闭包影响,它可以保证数据更新的准确性
  • 代码更直观

如何选择:

  • 新state 依赖于 旧state,那么我们应该用函数式更新
  • 如果没有依赖关系,我们则使用 直接传值的方式;

批处理

批处理指的是 React 会将多个状态更新合并 到一次重新渲染中,而不是每次 setState 都立即触发重新渲染。

原则

  • 事件处理器中的自动批处理

    React 事件处理器 (如 onClick、onChange)中,所有的 setState 调用都会被自动批处理。

    理解:同一个事件处理器中 的 setState 会被批处理,而不是单个单个执行

    同一个事件处理器

  • 生命周期和 useEffect 中的批处理

    在生命周期方法和 useEffect 中,更新也会被批处理。

  • React 18 的增强批处理

    React 18 之前:只有在 React 事件处理器中才有自动批处理。

    React 18 及以后 :批处理扩展到 所有场景,包括:

    • Promise
    • setTimeout
    • 原生事件处理程序
    • 其他异步代码
相关推荐
蜡台6 分钟前
Vue 打包优化
前端·javascript·vue.js·vite·vue-cli
木斯佳6 分钟前
前端八股文面经大全:快手前端一面 (2026-03-29)·面经深度解析
前端·宏任务·原型链·闭包
皙然21 分钟前
Redis配置文件(redis.conf)超详细详解
前端·redis·bootstrap
卷帘依旧1 小时前
JavaScript中this绑定问题详解
前端·javascript
dweizhao1 小时前
突发!Claude Code源码泄露了
前端
sunny_2 小时前
💥 Claude Code 源码泄露?我把这个最强 AI Coding Agent 的架构扒干净了
前端·agent·claude
西洼工作室2 小时前
React轮播图优化:通过延迟 + 动画的组合,彻底消除视觉上的闪烁感
前端·react.js·前端框架
yaaakaaang2 小时前
(八)前端,如此简单!---五组结构
前端·javascript
我是若尘2 小时前
我的需求代码被主干 revert 了,接下来我该怎么操作?
前端·后端·代码规范
魁首2 小时前
Claude Code 源码泄露的背后,到底与Codex,Gemini 有啥不一样?
前端·openai·claude