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
    • 原生事件处理程序
    • 其他异步代码
相关推荐
weixin_493503671 小时前
Vue3 前端生成 PDF:会员证书与活动签到表的三种打印方案与踩坑记录
前端·pdf·状态模式
Java陈序员4 小时前
轻量运维面板!一款现代化的服务器控制面板工具!
运维·服务器·python·react.js·github
尾善爱看海4 小时前
前端算法与手写题集
前端·算法
徐小夕4 小时前
JitWord 4.0 万字分享:从协同工具到AI Word操作系统,聊聊3年产品创业史
前端·vue.js·后端
萧鼎6 小时前
Python 高性能Web框架神器 FastAPI:自动生成API文、基于Pydant、异步请求处理全搞定
前端·python·fastapi
IT_陈寒7 小时前
Redis误用keys命令把生产环境搞崩了,血的教训
前端·人工智能·后端
计算机魔术师7 小时前
5000亿估值冲刺科创板,DeepSeek 为何急着上市?
前端
我血条子呢7 小时前
前端解析word方案
前端·word
kyriewen7 小时前
我用 AI 写完一个需求后才发现,最难的不是 prompt,而是验收
前端·程序员·ai编程
申行7 小时前
Vue 3 + DYMO Connect Framework 实战:从标签模板到打印服务的完整实现
前端