父组件useEffect与Context setState的状态更新延迟问题求助
组件在useEffect中调用Context提供的setLeftContent方法,卸载时在清理函数里再次调用该方法传入不同的code。Context中的setLeftContent会对比传入的code和当前state中的leftContentCode,不同则更新状态。但组件快速卸载重挂载时,Context的state还没完成更新,新的setLeftContent调用拿到的是旧的leftContentCode,导致判断错误,状态没有被正确更新。之前用setTimeout能解决但不够优雅,求更优方案。
问题代码
父组件useEffect代码
React.useEffect(() => { layout.setBackArrowUrl('/projects/list'); if (location.state && location.state.showArrow) { layout.setLeftContent( '/projects/project/monitor', <RecentProjectsList recentMode='search' />, ); layout.setBackArrowUrlSub('/projects/list'); } else { layout.setLeftContent('/projects/project/monitor', <RecentProjectsList recentMode='visit' />); layout.setBackArrowUrlSub(''); } return () => { layout.setBackArrowUrl(''); layout.setLeftContent('/projects/project/monitor/unmount', <React.Fragment />); }; }, []);
Context中的setLeftContent函数
setLeftContent = (code, leftDrawerContent) => { const { leftContentCode } = this.state; if (code !== leftContentCode) { this.setState({ leftDrawerContent, leftContentCode: code }); } };
问题原因
React的setState是异步操作,当组件快速卸载触发清理函数调用setLeftContent('/projects/project/monitor/unmount', ...)时,state更新还未完成;紧接着组件重挂载,useEffect里又调用setLeftContent('/projects/project/monitor', ...),此时函数中拿到的this.state.leftContentCode仍是更新前的旧值,和新传入的code对比后认为无需更新,导致状态停留在错误值。
解决方案
使用函数式setState替代直接依赖当前state的判断。函数式setState接收一个函数,参数为最新的state,能基于最新状态进行判断和更新,不受闭包旧值的影响。
修改后的Context代码:
setLeftContent = (code, leftDrawerContent) => { this.setState(prevState => { if (code !== prevState.leftContentCode) { return { leftDrawerContent, leftContentCode: code }; } // 无需更新时返回null或原state return null; }); };
方案说明
每次调用setLeftContent时,都会基于最新的prevState做判断,不管之前的setState是否完成,都能保证对比的是当前最新的状态值。即使组件快速卸载重挂载,两次setLeftContent调用都会基于最新状态处理,不会出现判断错误的情况。
内容的提问来源于stack exchange,提问作者Jan Novotny

