MUI Drawer的onClose函数多次触发问题求优化方案
解决MUI Drawer多次触发onClose导致重复请求的问题
核心思路
要确保onClose仅执行一次,核心是在请求处理期间拦截后续触发逻辑,同时不破坏正常的抽屉关闭交互。
方案1:独立状态控制执行锁(推荐)
这是最通用且职责清晰的方案,无需依赖Formik状态,单独维护一个“执行中”锁:
import { useState } from 'react'; import Drawer from '@mui/material/Drawer'; const YourComponent = () => { const [isOpen, setIsOpen] = useState(false); // 标记是否正在处理关闭逻辑 const [isClosing, setIsClosing] = useState(false); const handleClose = async () => { // 若正在处理,直接返回拦截重复触发 if (isClosing) return; setIsClosing(true); try { // 执行你的请求逻辑 await yourApiCall(); // 请求完成后关闭抽屉 setIsOpen(false); } catch (error) { console.error('关闭请求失败:', error); // 错误场景可灵活决定是否关闭抽屉,比如让用户留在页面修改 // setIsOpen(false); } finally { // 无论成功失败,释放锁 setIsClosing(false); } }; return ( <Drawer anchor="right" open={isOpen} onClose={handleClose}> {/* 抽屉内容 */} </Drawer> ); };
优势
- 逻辑独立,不与Formik的
isSubmitting耦合,避免业务状态混淆 - 明确区分“关闭处理中”和“表单提交中”,边界清晰
- 错误场景下可灵活控制抽屉状态
方案2:结合Formik状态优化(仅适用于关闭需提交表单的场景)
如果你的关闭逻辑和Formik表单提交强绑定,可优化原有方案,避免重复触发:
import { useState } from 'react'; import { useFormik } from 'formik'; import Drawer from '@mui/material/Drawer'; const YourComponent = () => { const [isOpen, setIsOpen] = useState(false); const formik = useFormik({ initialValues: {}, onSubmit: async (values) => { await yourSubmitApi(values); }, }); const handleClose = async () => { // 若Formik正在提交,直接拦截 if (formik.isSubmitting) return; try { // 触发Formik提交逻辑 await formik.submitForm(); setIsOpen(false); } catch (error) { console.error('提交失败:', error); } }; return ( <Drawer anchor="right" open={isOpen} onClose={handleClose}> <form onSubmit={formik.handleSubmit}> {/* 表单内容 */} </form> </Drawer> ); };
方案3:自定义Backdrop点击逻辑
利用MUI Drawer的onBackdropClick属性,单独接管外部点击的处理,避免原生多次触发:
import { useState } from 'react'; import Drawer from '@mui/material/Drawer'; const YourComponent = () => { const [isOpen, setIsOpen] = useState(false); const [isProcessing, setIsProcessing] = useState(false); const handleBackdropClick = async () => { if (isProcessing) return; setIsProcessing(true); try { await yourApiCall(); setIsOpen(false); } catch (error) { console.error('处理失败:', error); } finally { setIsProcessing(false); } }; return ( <Drawer anchor="right" open={isOpen} onClose={() => setIsOpen(false)} // 仅处理内部关闭(如点击关闭按钮) onBackdropClick={handleBackdropClick} // 单独处理外部点击 hideBackdrop={false} > {/* 抽屉内容 */} </Drawer> ); };
适用场景
需要区分“内部关闭”和“外部点击关闭”逻辑的场景,可精准控制外部点击的触发次数。
总结
优先选择方案1,它逻辑清晰、通用性强,不依赖第三方状态,能彻底解决重复执行问题。若业务与Formik提交强关联,再考虑方案2;需区分关闭来源时,方案3更合适。
内容的提问来源于stack exchange,提问作者Rostyk
相关产品推荐
相关产品推荐

