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

React中如何正确切换两个Bootstrap日历下拉框?

解决方案:React 18 下 Bootstrap 日历下拉联动实现

核心优化思路

放弃模拟点击按钮的方式,改用 Bootstrap 官方的 Dropdown JS API 控制下拉状态,同时针对 React 18 严格模式的双重挂载特性,完善 useEffect 的清理逻辑,让组件逻辑更清晰可靠。

重构后的代码实现

1. CalendarDropdown.js 组件

import { useEffect, useRef } from 'react';
import * as bootstrap from 'bootstrap'; // 若用CDN引入则不需要此import,直接用window.bootstrap

const CalendarDropdown = ({ date, onChange, open }) => {
  const dropdownContainerRef = useRef(null);
  const dropdownInstanceRef = useRef(null);
  const monthYear = dateHelper.dateToMonthYear(date);

  useEffect(() => {
    // 初始化Bootstrap Dropdown实例
    if (dropdownContainerRef.current && !dropdownInstanceRef.current) {
      dropdownInstanceRef.current = new bootstrap.Dropdown(dropdownContainerRef.current, {
        autoClose: 'outside'
      });
    }

    // 根据open prop控制下拉显隐
    const instance = dropdownInstanceRef.current;
    if (instance) {
      open ? instance.show() : instance.hide();
    }

    // 清理函数:处理React18双重挂载,销毁实例避免内存泄漏
    return () => {
      if (dropdownInstanceRef.current) {
        dropdownInstanceRef.current.dispose();
        dropdownInstanceRef.current = null;
      }
    };
  }, [open]);

  const handleMonthSelect = (selectedDate) => {
    onChange(selectedDate);
    // 选择日期后自动关闭当前下拉
    dropdownInstanceRef.current?.hide();
  };

  return (
    <div className="dropdown" style={styles.dropdownButtonWrapper} ref={dropdownContainerRef}>
      <button
        style={styles.dropdownButton}
        className="btn btn-secondary dropdown-toggle"
        type="button"
        data-bs-toggle="dropdown"
        aria-expanded={open}>
        <span>{monthYear}</span>
      </button>
      <ul className="dropdown-menu" onClick={e => e.stopPropagation()}>
        <Calendar
          maxDetail="year"
          value={date}
          onClickMonth={handleMonthSelect} />
      </ul>
    </div>
  );
};

2. App.js 父组件

import { useState } from 'react';

const App = ({ startDate, endDate, setStartDate, setEndDate }) => {
  const [isStartDropdownOpen, setIsStartDropdownOpen] = useState(false);
  const [isEndDropdownOpen, setIsEndDropdownOpen] = useState(false);

  const handleStartDateChange = (date) => {
    setStartDate(date);
    // 关闭第一个下拉,打开第二个
    setIsStartDropdownOpen(false);
    setIsEndDropdownOpen(true);
  };

  const handleEndDateChange = (date) => {
    setEndDate(date);
    setIsEndDropdownOpen(false);
  };

  return (
    <div className="input-group">
      <CalendarDropdown
        date={startDate}
        onChange={handleStartDateChange}
        open={isStartDropdownOpen} />
      <CalendarDropdown
        date={endDate}
        onChange={handleEndDateChange}
        open={isEndDropdownOpen} />
    </div>
  );
};

关键优化点说明

  • 用官方API替代模拟点击:直接调用 Bootstrap Dropdown 的 show()/hide() 方法控制状态,避免手动点击按钮带来的不可控问题,适配 React 18 严格模式的双重挂载场景。
  • 完善清理逻辑:useEffect 返回的清理函数会销毁 Dropdown 实例,防止内存泄漏,同时处理严格模式下的重复挂载。
  • 简化组件通信:移除原代码中 onChange 返回值的别扭写法,父组件直接通过状态控制两个下拉的联动,逻辑更清晰。
  • 正确设置 aria 属性:将按钮的 aria-expanded 与 open prop 绑定,符合无障碍访问规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 06:11:16