React事件监听器回调中调用props.updateOrder未更新props.index问题
问题原因
这是典型的React闭包陷阱导致的问题:
- 你的
useEffect依赖数组为[],只会在组件首次挂载时执行一次,绑定的handleKeyDown、handleKeyUp都是首次渲染时创建的函数版本 - 这两个函数创建时捕获的
props.index、props.updateOrder都是初始值,后续组件重新渲染、props更新后,已经绑定在window上的事件回调仍然使用旧闭包内的数据,无法拿到最新的props,因此调用updateOrder不会按预期生效 - 组件内的
onClick回调每次渲染都会生成最新版本,能拿到实时的props,所以调用正常
解决方案
方案1:补全useEffect依赖
将事件回调加入useEffect依赖数组,依赖变化时自动重新绑定最新的事件处理函数:
useEffect(() => { window.addEventListener("keydown", handleKeyDown); window.addEventListener("keyup", handleKeyUp); return () => { window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keyup", handleKeyUp); }; }, [handleKeyDown, handleKeyUp]);
可以搭配useCallback包裹事件处理函数,避免不必要的重复绑定:
const handleKeyUp = useCallback((e) => { const keys = { 39: () => { e.preventDefault(); setImage(polygons.polygonDefaultRight); props.updateOrder(props.orders, props.index, 'next'); console.log('Right', props.index); } }; keys[e.keyCode]?.(); }, [props, setImage]); const handleKeyDown = useCallback((e) => { const keys = { 39: () => { e.preventDefault(); setImage(polygons.polygonHoverRight); } }; keys[e.keyCode]?.(); }, [setImage]);
方案2:用ref存储最新props
如果不想频繁重新绑定事件,可以用ref存储实时的props值,事件回调从ref中取值,避开闭包限制:
// 定义ref保存最新props const propsRef = useRef(props); // 每次渲染更新ref内容 useEffect(() => { propsRef.current = props; }, [props]); const handleKeyUp = (e) => { const keys = { 39: () => { e.preventDefault(); setImage(polygons.polygonDefaultRight); const { updateOrder, orders, index } = propsRef.current; updateOrder(orders, index, 'next'); console.log('Right', index); } }; keys[e.keyCode]?.(); };
该方案下原useEffect的空依赖数组无需修改,也能正常运行。
内容的提问来源于stack exchange,提问作者Madushan Lamahewa
相关产品推荐
相关产品推荐

