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
    • 原生事件处理程序
    • 其他异步代码
相关推荐
橙子家1 小时前
浏览器缓存之【基础键值存储】:Local storage 和 Session storage
前端
星星在线3 小时前
MusicFree:一个「All in One」的个人音乐服务器,让听歌回归简单
前端·后端
IT_陈寒4 小时前
Redis的SETNX并发问题让我加了三天班
前端·人工智能·后端
demo007x5 小时前
Docling 文档转换以及技术架构分析
前端·后端·程序员
京东云开发者5 小时前
京东市民服务又“上新”!这次是黑龙江“龙易办”
前端
袋鱼不重6 小时前
我的神奇同事,AI 用多了居然写了个 Open In Codex
前端·后端·ai编程
Fireworks6 小时前
深入vue3源码解读 -- 1、响应式的基础概念
前端
程序员黑豆6 小时前
JDK 下载安装与配置详细教程
java·前端·ai编程
hunterandroid7 小时前
文件存储:内部存储与外部存储
前端
NorBugs7 小时前
飞机大战 Low 版 (Made in AI)
前端