React中useState的setter两种使用方式的区别及函数式更新的生效原因
useState 的 setter 调用方式差异说明
示例代码
import React, { useState } from 'react'; function Example() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Update 1 </button> <button onClick={() => setCount(prevCount => prevCount + 1)}> Update 2 </button> </div> ); }
核心结论先明确
第二种传入函数的写法不是未文档化的非官方特性,是React从Hooks功能首次发布时就正式支持的标准能力,只是很多入门文档的章节只讲了基础的传值用法,没有展开说明函数式更新的场景,才会让人误以为这是私下的特殊写法。
两种调用方式的本质差异
- 第一种是值式更新:直接把传入的内容作为新的状态值使用,状态计算依赖当前组件渲染周期内,闭包捕获到的旧状态值。
- 第二种是函数式更新:传入一个纯函数作为状态更新器,React会把这个更新器放进状态更新队列,依次以上一次更新完成后的真实最新状态作为入参执行函数,将函数的返回值作为新的状态值。
为什么示例里两个按钮效果完全一样
因为两个按钮的点击逻辑都是单次触发一次setter,不存在连续多次叠加更新的场景,两种写法计算出来的新状态值没有区别,表现出来的效果自然完全一致。
两种方式的效果分歧场景
当同一个执行上下文里连续多次调用setter时,差异就会立刻显现,这也是很多开发者刚接触React时踩过的经典坑:
- 值式更新因为共享同一个闭包里的旧值,连续多次叠加计算会失效。比如同一个点击事件里连续写3次
setCount(count + 1),最终count只会从0变成1,不会变成3——因为三次计算都是基于闭包里捕获的初始值0完成的,根本拿不到中间更新的状态。 - 函数式更新因为把每一次计算都放进队列依次执行,能保证每一步计算都基于上一次的真实结果,连续写3次
setCount(prevCount => prevCount + 1),就能正确让count从0累加到3。
关于你看到的用法来源
Redux教程里提到的这种函数写法,本质是因为Redux的核心reducer本身就是“接收旧状态、返回新状态”的纯函数设计,和React内置的函数式更新逻辑完全匹配,并不是Redux自定义的特殊用法。
补充说明:setter对入参的判断是React层面内置的实现逻辑,不属于JavaScript语言本身的能力:当setter检测到传入的参数是函数时,就走队列执行的函数式更新逻辑;如果传入的不是函数,就直接把入参作为新状态和旧值做对比,决定是否触发重渲染。
内容的提问来源于stack exchange,提问作者omikron
相关产品推荐
相关产品推荐

