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
    • 原生事件处理程序
    • 其他异步代码
相关推荐
吴文周44 分钟前
告别重复劳动:一套插件让 AI 替你写代码、修Bug、做测试、上生产
前端·后端·ai编程
Mh1 小时前
我决定写一个 3D 地球仪来记录下我要去的地方
前端·javascript·动效
yaoxin5211231 小时前
390. Java IO API - WatchDir 示例
java·前端·python
懒狗小前端1 小时前
做了一个 codex 的中文文档网站,做的不好可以随便喷
前端·后端
. . . . .3 小时前
ref、useRef 和 forwardRef
前端·javascript·react.js
energy_DT3 小时前
2026年海上钻井平台数字孪生平台:引领海洋能源数字化转型
前端
Eric_见嘉3 小时前
在职前端 Agent 配置分享
前端·后端·agent
柚子8163 小时前
break跳出语句块的神奇技巧
前端·javascript
ejinxian5 小时前
Rust GUI框架Azul与Electron、WebView2
前端·javascript·electron
IT_陈寒5 小时前
Vue的v-for里用index当key,我被自己坑惨了
前端·人工智能·后端