React中useCallback回调访问Hooks状态为undefined如何解决?
问题解答
可以在useCallback包裹的回调中访问Hooks定义的状态变量吗?
完全可以。你现在拿到undefined是两个问题叠加导致的:
- 首先是事件名拼写错误:你给输入框绑定的是
onChnage,React输入框的变更事件正确写法是onChange,字母顺序写反会导致输入时的setState逻辑根本不触发,状态从一开始就没被正确赋值。 - 其次是踩了useCallback的闭包陷阱:你给useCallback传的依赖数组是空的,意味着这个回调只会在组件第一次挂载的时候生成一次,生成时捕获的是当时上下文里的test1、test2值,后续哪怕状态更新了,这个回调也不会重新创建,永远拿不到最新值。要是你写useState的时候没显式传空字符串当初始值,初始值默认就是undefined,刚好就会出现你现在打印结果全是undefined的情况。
修复步骤
先改对事件绑定
把输入框上拼错的事件名改成正确的onChange,保证输入时能正常更新状态:
<input placeholder="输入test1" onChange={(e)=>setTest1(e.target.value)} /> <input placeholder="输入test2" onChange={(e)=>setTest2(e.target.value)} />
再修复useCallback的取值问题,三种方案选一个即可
- 按官方规范补全依赖数组(优先推荐)
把回调里用到的所有外部状态、函数都加到useCallback的依赖数组里,只要依赖项变化,回调就会重新生成,自然能拿到最新的状态值:const onHandleSubmit = useCallback( (event) => { event.preventDefault(); // 一定要加,阻止浏览器默认表单刷新行为 console.log(test1) console.log(test2) const data = { test1, test2 } dispatch({type: 'send/data', data}); },[test1, test2, dispatch]); // 用到的test1、test2、dispatch都加入依赖 - 简单场景直接去掉useCallback
要是你这个提交函数只在当前组件里用,没有传给memo包裹的优化子组件当props,完全没必要套useCallback,直接写普通函数就行,每次组件渲染都会生成新的函数,闭包捕获的永远是当次渲染的最新状态:const onHandleSubmit = (event) => { event.preventDefault(); console.log(test1) console.log(test2) const data = { test1, test2 } dispatch({type: 'send/data', data}); }; - 特殊场景用useRef存最新值(不推荐常规使用)
如果你确实不想让回调函数因为状态变化重新生成,可以用useRef同步存一份最新的状态,回调里读ref的current属性就能拿到最新值,绕开闭包限制:const [test1, setTest1] = useState('') const [test2, setTest2] = useState('') // 创建ref存储状态 const test1Ref = useRef(test1) const test2Ref = useRef(test2) // 每次状态更新同步给ref useEffect(() => { test1Ref.current = test1 test2Ref.current = test2 }, [test1, test2]) const onHandleSubmit = useCallback( (event) => { event.preventDefault(); // 从ref上取最新值 const data = { test1: test1Ref.current, test2: test2Ref.current } dispatch({type: 'send/data', data}); },[dispatch]);
踩坑提示:不要为了应付eslint的依赖告警随便把依赖数组置空,90%的Hooks闭包问题都是这么来的。
内容的提问来源于stack exchange,提问作者Edgar
相关产品推荐
相关产品推荐

