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

如何控制Material UI ClickAway,避免模态框提交时触发?

解决ClickAwayListener在模态框内触发的问题

问题核心在于:ClickAwayListener会把模态框判定为"点击外部"(因为模态框通常挂载在body或独立容器中,不属于DataGrid的父级范围),导致点击模态框提交按钮时触发清除逻辑。下面提供两种可靠的解决方案:

方案一:通过Ref判断点击目标是否属于模态框

利用DOM元素的contains方法,判断点击事件的目标是否在模态框内部,若是则跳过清除操作:

  1. 父组件中为模态框创建Ref
import React, { useRef, useState } from 'react';
import MyTable from './MyTable';
import EditJobModal from './EditJobModal';

const ParentComponent = () => {
  const modalRef = useRef(null);
  const [isModalOpen, setIsModalOpen] = useState(false);
  // 其他业务逻辑...

  return (
    <>
      <MyTable 
        columns={columns} 
        data={data} 
        childToParent={childToParent} 
        modalRef={modalRef} 
      />
      <EditJobModal 
        ref={modalRef} 
        open={isModalOpen} 
        onClose={() => setIsModalOpen(false)} 
        // 模态框其他属性
      />
    </>
  );
};
  1. 修改MyTable组件的handleClickAway逻辑
import React, { useState } from 'react';
import { ClickAwayListener } from '@mui/base';
import { DataGrid, GridToolbar} from '@mui/x-data-grid';

const MyTable = ({columns, data, childToParent, modalRef}) => {
    const [selectionModel, setSelectionModel] = useState([]);
    const [selectedRows, setSelectedRows] = useState([]);

    const handleClickAway = (event) => {
        // 检查点击目标是否在模态框内部,是则直接返回
        if (modalRef.current && modalRef.current.contains(event.target)) {
            return;
        }
        const emptyRow = [{id: ''}];
        setSelectionModel([]);
        setSelectedRows(emptyRow);
        childToParent(emptyRow);
    };

    return (
        <div style={{ height: 600, width: '100%' }}>
            <ClickAwayListener onClickAway={handleClickAway}>
                <DataGrid
                    columns={columns}
                    rows={data}
                    pageSize={5}
                    rowsPerPageOptions={[10]}
                    checkboxSelection={false}
                    components={{ Toolbar: GridToolbar }}
                    selectionModel={selectionModel}
                    onSelectionModelChange={(ids) => {
                        if (ids.length > 1) {
                            let selectionSet = new Set(selectionModel);
                            let result = ids.filter((s) => !selectionSet.has(s));
                            setSelectionModel(result);
                        } else {
                            let selectedIDs = new Set(ids);
                            let selectedRows = data.filter((row) =>
                                selectedIDs.has(row.id),
                            );
                            setSelectedRows(selectedRows);
                            setSelectionModel(ids[0]);
                            childToParent(selectedRows);
                        }              
                    }}
                />
            </ClickAwayListener> 
        </div>
    );
};

export default MyTable;

方案二:根据模态框状态控制ClickAway逻辑

在父组件中维护模态框的打开状态,传递给MyTable,让ClickAway逻辑在模态框打开时失效:

  1. 父组件传递模态框状态
const ParentComponent = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);
  // 其他业务逻辑...

  return (
    <>
      <MyTable 
        columns={columns} 
        data={data} 
        childToParent={childToParent} 
        isModalOpen={isModalOpen} 
      />
      <EditJobModal 
        open={isModalOpen} 
        onClose={() => setIsModalOpen(false)} 
        // 模态框其他属性
      />
    </>
  );
};
  1. 修改MyTable的ClickAway逻辑

方式A:在handleClickAway中判断状态

const MyTable = ({columns, data, childToParent, isModalOpen}) => {
    // ...其他状态定义

    const handleClickAway = () => {
        // 模态框打开时不执行清除操作
        if (isModalOpen) return;
        const emptyRow = [{id: ''}];
        setSelectionModel([]);
        setSelectedRows(emptyRow);
        childToParent(emptyRow);
    };

    // ...其余代码不变
};

方式B:条件渲染ClickAwayListener

const MyTable = ({columns, data, childToParent, isModalOpen}) => {
    // ...其他状态定义

    const handleClickAway = () => {
        const emptyRow = [{id: ''}];
        setSelectionModel([]);
        setSelectedRows(emptyRow);
        childToParent(emptyRow);
    };

    return (
        <div style={{ height: 600, width: '100%' }}>
            {isModalOpen ? (
                <DataGrid 
                    columns={columns}
                    rows={data}
                    pageSize={5}
                    rowsPerPageOptions={[10]}
                    checkboxSelection={false}
                    components={{ Toolbar: GridToolbar }}
                    selectionModel={selectionModel}
                    onSelectionModelChange={(ids) => {
                        if (ids.length > 1) {
                            let selectionSet = new Set(selectionModel);
                            let result = ids.filter((s) => !selectionSet.has(s));
                            setSelectionModel(result);
                        } else {
                            let selectedIDs = new Set(ids);
                            let selectedRows = data.filter((row) =>
                                selectedIDs.has(row.id),
                            );
                            setSelectedRows(selectedRows);
                            setSelectionModel(ids[0]);
                            childToParent(selectedRows);
                        }              
                    }}
                />
            ) : (
                <ClickAwayListener onClickAway={handleClickAway}>
                    <DataGrid 
                        columns={columns}
                        rows={data}
                        pageSize={5}
                        rowsPerPageOptions={[10]}
                        checkboxSelection={false}
                        components={{ Toolbar: GridToolbar }}
                        selectionModel={selectionModel}
                        onSelectionModelChange={(ids) => {
                            if (ids.length > 1) {
                                let selectionSet = new Set(selectionModel);
                                let result = ids.filter((s) => !selectionSet.has(s));
                                setSelectionModel(result);
                            } else {
                                let selectedIDs = new Set(ids);
                                let selectedRows = data.filter((row) =>
                                    selectedIDs.has(row.id),
                                );
                                setSelectedRows(selectedRows);
                                setSelectionModel(ids[0]);
                                childToParent(selectedRows);
                            }              
                        }}
                    />
                </ClickAwayListener>
            )}
        </div>
    );
};

额外优化点

原代码中handleClickAway里的childToParent(selectedRows)传递的是旧状态(因为setSelectedRows是异步更新),建议直接传递新的空行对象,避免数据不一致问题。

内容的提问来源于stack exchange,提问作者gwydion93

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:16:08