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

Material UI两个TextField的Popper互斥关闭功能实现问题

问题描述

我现在并排实现了两个TextField组件,它们的end adornment位置都设置了按钮,点击这些按钮可以切换对应Popper的显隐状态。同时每个Popper都绑定了ClickAwayListener,实现点击Popper外部区域时自动关闭Popper的效果。

我的需求是:当第一个Popper处于打开状态时,点击第二个TextField的adornment按钮,需要自动关闭第一个Popper。

遇到的问题

我在end adornment的按钮点击事件中调用了stopPropagation阻止事件冒泡,避免点击按钮时触发ClickAwayListener导致Popper刚打开就立刻关闭,但这也导致点击另一个TextField的按钮时,已打开的Popper无法接收到点击外部的事件,不会自动关闭。

我尝试过把TextField包裹在ClickAwayListener内部,但没有生效。

补充说明

两个TextField是由独立的组件渲染的,我不希望在它们之间传递任何props,要保证组件的独立性。

原始代码

const [firstPopperVisible, setFirstPopperVisible] = React.useState(false);
const [secondPopperVisible, setSecondPopperVisible] = React.useState(false);
const firstTextFieldRef = React.useRef();
const secondTextFieldRef = React.useRef();

const toggleFirstPopperVisible = (e) => {
  e.stopPropagation();
  setFirstPopperVisible((prev) => !prev);
};
const handleFirstPopperClickAway = (e) => {
  setFirstPopperVisible(false);
};
const toggleSecondPopperVisible = (e) => {
  e.stopPropagation();
  setSecondPopperVisible((prev) => !prev);
};
const handleSecondPoppertClickAway = (e) => {
  setSecondPopperVisible(false);
};
return (
  <div style={{ display: "flex", flexDirection: "row" }}>
    <div>
      <TextField
        label="Outlined"
        variant="outlined"
        inputRef={firstTextFieldRef}
        InputProps={{
          endAdornment: (
            <InputAdornment position="end">
              <IconButton
                aria-label="toggle password visibility"
                edge="end"
                onClick={toggleFirstPopperVisible}
              >
                <Visibility />
              </IconButton>
            </InputAdornment>
          )
        }}
      />
      <ClickAwayListener onClickAway={handleFirstPopperClickAway}>
        <Popper
          open={firstPopperVisible}
          anchorEl={firstTextFieldRef.current}
          placement="bottom-start"
        >
          Content
        </Popper>
      </ClickAwayListener>
    </div>
    <div>
      <TextField
        label="Outlined"
        variant="outlined"
        inputRef={secondTextFieldRef}
        InputProps={{
          endAdornment: (
            <InputAdornment position="end">
              <IconButton
                aria-label="toggle password visibility"
                edge="end"
                onClick={toggleSecondPopperVisible}
              >
                <Visibility />
              </IconButton>
            </InputAdornment>
          )
        }}
      />
      <ClickAwayListener onClickAway={handleSecondPoppertClickAway}>
        <Popper
          open={secondPopperVisible}
          anchorEl={secondTextFieldRef.current}
          placement="bottom-start"
        >
          Content
        </Popper>
      </ClickAwayListener>
    </div>
  </div>
);
}

临时解决方案

我找到了一个临时解决方案,把TextField包裹在div中,再把这个div包裹在ClickAwayListener内部,同时在需要的Popper自身上阻止事件冒泡。这个方案并不完美,但符合我的业务需求。

<ClickAwayListener onClickAway={handleFirstPopperClickAway}>
<div style={{display: inline-box}>
<TextField>
.....
</TextField>
</div>
</ClickawayListener>
<Popper>
....
</Popper>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 12:18:04