React组件中Cytoscape事件处理内获取最新state的方法
解决Cytoscape事件处理函数中获取陈旧React State的问题
这是闭包特性导致的典型问题:事件处理函数在初始化绑定阶段捕获了当时的state值,后续state更新后,旧的处理函数依然引用最初的state快照。下面是几种可行的解决方法:
方案1:用useRef同步最新state
useRef的current属性是可变的,不会被闭包锁定,我们可以在state更新时同步它的值,让事件处理函数随时能拿到最新的state:
const [otherState, setOtherState] = useState(); const otherStateRef = useRef(otherState); // 每当otherState更新,同步到ref中 useEffect(() => { otherStateRef.current = otherState; }, [otherState]); const handleMouseOverNode = (e) => { // 从ref的current属性获取最新值 console.log(otherStateRef.current); }; useEffect(() => { if (!cyRef.current) return; cyRef.current.on('mouseover', 'node', handleMouseOverNode); return () => { cyRef.current.removeListener('mouseover', 'node', handleMouseOverNode); }; }, [cyRef.current]);
方案2:在useEffect内部定义事件处理函数
把事件处理函数直接定义在绑定事件的useEffect内部,这样每次useEffect执行(依赖变化时),都会创建新的处理函数,自然能捕获到最新的state:
const [otherState, setOtherState] = useState(); useEffect(() => { if (!cyRef.current) return; const handleMouseOverNode = (e) => { console.log(otherState); // 直接拿到最新的state }; cyRef.current.on('mouseover', 'node', handleMouseOverNode); return () => { cyRef.current.removeListener('mouseover', 'node', handleMouseOverNode); }; }, [cyRef.current, otherState]); // 把otherState加入依赖数组,state更新时重新绑定事件
方案3:用useCallback包裹处理函数
用useCallback包裹事件处理函数,并将otherState加入依赖数组,这样每次state更新时,useCallback会返回新的函数实例,触发useEffect重新绑定事件,确保处理函数引用最新的state:
const [otherState, setOtherState] = useState(); const handleMouseOverNode = useCallback((e) => { console.log(otherState); }, [otherState]); // 依赖otherState,state更新时函数自动更新 useEffect(() => { if (!cyRef.current) return; cyRef.current.on('mouseover', 'node', handleMouseOverNode); return () => { cyRef.current.removeListener('mouseover', 'node', handleMouseOverNode); }; }, [cyRef.current, handleMouseOverNode]); // 依赖更新后的处理函数
注意事项
- 移除事件监听时,必须和绑定的是同一个函数实例,否则会出现监听残留导致内存泄漏。方案2和3中,每次重新绑定前都会先移除旧的监听,避免这个问题;方案1中处理函数不变,只需绑定一次。
内容的提问来源于stack exchange,提问作者Chen Peleg
相关产品推荐
相关产品推荐

