React状态变更后依据不同触发事件同步执行对应处理函数的最优方案
方案1:利用弹窗组件自带的关闭后回调(优先推荐)
绝大多数成熟的弹窗组件(比如Ant Design、MUI的Modal)都会提供afterClose/onExited这类回调,会在弹窗完全隐藏(包含关闭动画执行完毕)、状态更新完成后触发,直接把对应逻辑传进去即可,代码改动最小。
如果是你自行封装的DialogModal组件,也可以新增该回调属性,在弹窗关闭逻辑执行完成后触发即可。
方案2:新增状态存储待执行回调,配合useEffect实现(通用无依赖方案)
如果不想修改弹窗组件逻辑,可以用这个通用方案,核心是把关闭后要执行的回调暂存,监听弹窗关闭状态后触发,完全遵循React的状态更新规则:
const Parent = () => { const [isDialogShown, setIsDialogShown] = useState(false); // 存储弹窗关闭后要执行的回调 const [afterCloseAction, setAfterCloseAction] = useState(null); const focusOnElementA = () => { /* 具体逻辑 */ }; const focusOnElementB = () => { /* 具体逻辑 */ }; const handleDismiss = () => { setIsDialogShown(false); // 这里用函数形式赋值,避免回调被立即执行 setAfterCloseAction(() => focusOnElementA); } const handleConfirm = () => { setIsDialogShown(false); setAfterCloseAction(() => focusOnElementB); } // 监听弹窗关闭状态,执行对应逻辑 useEffect(() => { if (!isDialogShown && afterCloseAction) { afterCloseAction(); // 执行后清空,避免重复触发 setAfterCloseAction(null); } }, [isDialogShown, afterCloseAction]); return ( <> <Button onClick={() => { setIsDialogShown(true) }}>打开弹窗</Button> <DialogModal isOpen={isDialogShown} onDismiss={handleDismiss} onConfirm={handleConfirm} /> </> ) }
如果想减少不必要的重渲染,可以把afterCloseAction换成useRef存储,逻辑完全一致,不需要将ref加入useEffect的依赖数组。
原写法失效原因说明
React的状态更新是批量异步执行的,原写法中执行setIsDialogShown(false)后立即调用焦点定位函数时,isDialogShown状态还未更新,弹窗仍存在于DOM中,多数弹窗自带的焦点锁定逻辑会拦截焦点移动操作,导致定位失败。
内容的提问来源于stack exchange,提问作者Stephen Koo
相关产品推荐
相关产品推荐

