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

React使用createRef为div设置焦点时下拉菜单点击无法关闭问题

React自定义下拉组件点击失效问题修复

问题根因

  • 事件时序与逻辑绑定错误:将下拉框的onBlur事件直接绑定了状态切换(toggle)方法,点击触发器或下拉选项时,事件执行顺序为「下拉框先失焦触发blur→状态切为收起→点击事件触发→状态又切为展开」,最终表现为下拉框无法收起,状态反复跳变看起来像重复渲染。
  • Ref创建时机错误:createRef()写在了componentDidMount生命周期中,组件首次挂载时ref未完成初始化,会导致焦点绑定逻辑偶发异常,放大状态错乱问题。
  • 选项点击逻辑缺失:下拉选项的点击事件仅处理了币种切换,没有主动触发下拉收起,完全依赖blur事件执行,本身存在时序隐患。
  • 缺少点击范围判断:blur事件触发时没有校验焦点跳转的目标位置,组件内部元素点击导致的焦点转移也会触发收起逻辑,和内部点击事件产生冲突。

修复方案

  • 将所有ref的创建移到constructor中,保证组件初始化时ref即可正常绑定DOM节点
  • 拆分状态逻辑:触发器点击保留toggle逻辑,blur事件、选项选中事件单独绑定显式收起逻辑,不再复用toggle方法
  • blur事件增加范围校验:失焦后如果焦点仍落在下拉组件或触发器内部,不执行收起逻辑
  • 选项选中后主动调用收起方法,不依赖blur事件触发状态更新

修复后核心代码

class NavBar extends Component {
  constructor(props) {
    super(props);
    this.state = {
      showCurrencies: false,
    };
    // ref统一在构造函数创建
    this.currencyRef = createRef();
    this.switchRef = createRef();

    this.handleToggleCurrencies = this.handleToggleCurrencies.bind(this);
    this.handleCloseCurrencies = this.handleCloseCurrencies.bind(this);
  }

  componentDidUpdate(prevProps, prevState) {
    // 展开时自动聚焦下拉框
    if (this.state.showCurrencies && !prevState.showCurrencies) {
      this.currencyRef.current?.focus();
    }
  }

  // 触发器专用切换逻辑
  handleToggleCurrencies() {
    this.setState({
      showCurrencies: !this.state.showCurrencies,
    });
  }

  // 通用收起逻辑
  handleCloseCurrencies() {
    this.setState({
      showCurrencies: false,
    });
  }

  render() {
    return (
      <>
        <div 
          className="currency-switch" 
          ref={this.switchRef}
          onClick={this.handleToggleCurrencies}
        >
          {currencySymbol}
          <span>
            <button>
              <img src={`${process.env.PUBLIC_URL}/images/arrow.png`} />
            </button>
          </span>
        </div>
        {this.state.showCurrencies ? (
          <div
            className="dropdown"
            tabIndex={"0"}
            ref={this.currencyRef}
            onBlur={(e) => {
              // 焦点仍在组件内部时不收起
              const { currentTarget, relatedTarget } = e;
              if (
                currentTarget.contains(relatedTarget) || 
                this.switchRef.current.contains(relatedTarget)
              ) {
                return;
              }
              this.handleCloseCurrencies();
            }}
          >
            {currencies?.map((currency) => (
              <div
                key={currency.symbol}
                className={`dropdown-items ${currencySymbol === currency.symbol ? "selected" : "" }`}
                onClick={() => {
                  changeCurrencySymbol(currency.symbol);
                  // 选完选项主动收起
                  this.handleCloseCurrencies();
                }}
              >
                {`${currency.symbol} ${currency.label}`}
              </div>
            ))}
          </div>
        ) : null}
      </>
    )
  }
}

修复后三个交互逻辑均可正常运行:点击触发器切换展开/收起、选中选项自动收起、点击组件外部自动收起,不会再出现状态反复跳变的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:39:10