React state在useCallback回调内无法更新的问题如何解决?
问题原因
你遇到的是React经典的闭包陷阱问题:
- 你给
useCallback传入了空依赖数组,因此这个回调函数会在组件首次渲染后被永久缓存,内部捕获的alpha3Code_永远是初始值'gbr' - 实际上
set_alpha3Code_本身是可以正常更新state的,你觉得更新失败只是因为回调内打印的alpha3Code_是旧的闭包值,你可以在组件顶层打log验证state已经正常变化。 - 如果你直接把
alpha3Code_加到useCallback的依赖数组,每次state更新都会重新生成回调函数,你套在AutocompleteCountries上的memo缓存就会完全失效,达不到避免重复渲染的优化目的。
解决方案
方案1: setState函数式更新(最简便,无额外依赖)
如果你的回调里只需要更新state,不需要拿到最新的alpha3Code_做额外逻辑,直接用函数式更新写法即可,不需要修改useCallback的依赖:
let AC = memo(AutocompleteCountries) function Show(props: {}) { let [alpha3Code_, set_alpha3Code_] = useState<string>('gbr'); let onChange = useCallback((alpha3Code) => { // 函数式更新可以直接拿到最新state,不需要依赖外部变量 set_alpha3Code_(() => alpha3Code); }, []); return ( <div> <AC onChange={onChange}/> {alpha3Code_ ? <Country cca3_={alpha3Code_}/> : null} </div> ) }
方案2:useRef同步最新状态(兼容所有React版本)
如果你需要在回调里读取最新的alpha3Code_做其他逻辑,可以用ref存储最新状态值,不影响useCallback的缓存:
let AC = memo(AutocompleteCountries) function Show(props: {}) { let [alpha3Code_, set_alpha3Code_] = useState<string>('gbr'); // 用ref同步state最新值 const alpha3CodeRef = useRef(alpha3Code_); useEffect(() => { alpha3CodeRef.current = alpha3Code_; }, [alpha3Code_]) let onChange = useCallback((alpha3Code) => { // 读ref即可拿到最新值,无闭包问题 console.log(alpha3CodeRef.current); set_alpha3Code_(alpha3Code); }, []); return ( <div> <AC onChange={onChange}/> {alpha3Code_ ? <Country cca3_={alpha3Code_}/> : null} </div> ) }
方案3:useEvent(React 18+ 官方方案)
如果你使用React 18及以上版本,可以直接用内置的useEvent定义回调,永远保持引用不变的同时,内部可以读取到最新的state和props:
import { useEvent } from 'react'; let AC = memo(AutocompleteCountries) function Show(props: {}) { let [alpha3Code_, set_alpha3Code_] = useState<string>('gbr'); const onChange = useEvent((alpha3Code) => { console.log(alpha3Code_); set_alpha3Code_(alpha3Code); }); return ( <div> <AC onChange={onChange}/> {alpha3Code_ ? <Country cca3_={alpha3Code_}/> : null} </div> ) }
内容的提问来源于stack exchange,提问作者George
相关产品推荐
相关产品推荐

