React中setState函数在组件渲染时是否会改变引用?
问题
在React/JavaScript中,闭包会让函数保留对外部变量的引用;组件渲染时状态变量的引用会改变,导致闭包捕获的旧引用失效。但setState这类状态更新函数在每次渲染时是否也会改变引用?
在下方的Navbar组件中,setCount被传入scrollHandler并绑定为window滚动事件监听,若setCount引用随渲染改变,旧引用应无法更新UI,但实际并非如此。另外,使用setCount(count + 1)和setCount((count) => count + 1)两种方式时,前者依赖闭包捕获的旧count,后者能获取当前最新count,若setCount引用未变,为何会有这样的差异?
附代码示例:
import React, { useEffect, useState } from 'react' import './Navbar.css'; import logo from '../../assets/logo.png' const Navbar = () => { const [count, setCount] = useState(0); console.log("Count is " + count); useEffect(() => { const scrollHandler = () => { console.log("Handler is called"); setCount((count) => count + 1); } window.addEventListener('scroll', scrollHandler); }, []) return ( <></> ) } export default Navbar
回答
1. setState的引用是否会随渲染变化?
在React中,由useState返回的状态更新函数(比如setCount),其引用在组件的整个生命周期中是保持不变的。React会确保这一点,所以即使组件多次重新渲染,你拿到的setCount始终是同一个函数引用。这也就解释了示例中第一次渲染时绑定的scrollHandler里的setCount,在后续滚动触发时依然能正常更新状态——因为它的引用没变化,一直指向有效的状态更新函数。
2. 两种setCount写法的差异原因
这两种写法的核心区别在于获取状态的方式不同,和setCount本身的引用无关:
setCount(count + 1):这里的count是闭包捕获的当前渲染周期的状态值。如果事件监听是在第一次渲染时绑定的(比如示例中useEffect的依赖为空数组),闭包捕获的永远是第一次渲染时的count(也就是0),每次调用都会把状态设置为0 + 1 = 1,后续再调用也不会再增加。setCount((prevCount) => prevCount + 1):这种函数式更新的方式,React会自动把当前最新的状态值作为参数传入回调函数。不管事件监听是何时绑定的,每次调用这个回调时,拿到的prevCount都是当前最新的状态值,所以能正确实现累加。
简单来说,函数式更新绕过了闭包捕获旧状态的问题,直接从React内部获取最新状态,两种写法的差异和setCount的引用是否变化没有关系,只和状态值的获取途径有关。
内容的提问来源于stack exchange,提问作者Ronak Jain
相关产品推荐
相关产品推荐

