You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 09:35:34