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
    • 原生事件处理程序
    • 其他异步代码
相关推荐
mCell4 小时前
如何零成本搭建个人站点
前端·程序员·github
mCell5 小时前
为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞
前端·设计模式·agent
恋猫de小郭5 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
少云清5 小时前
【安全测试】2_客户端脚本安全测试 _XSS和CSRF
前端·xss·csrf
银烛木5 小时前
黑马程序员前端h5+css3
前端·css·css3
m0_607076605 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
听海边涛声5 小时前
CSS3 图片模糊处理
前端·css·css3
IT、木易5 小时前
css3 backdrop-filter 在移动端 Safari 上导致渲染性能急剧下降的优化方案有哪些?
前端·css3·safari
0思必得06 小时前
[Web自动化] Selenium无头模式
前端·爬虫·selenium·自动化·web自动化
anOnion6 小时前
构建无障碍组件之Dialog Pattern
前端·html·交互设计