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

React+Node.js中父下拉框变更时子下拉框联动异常问题

问题诊断与修复方案

核心问题:React受控组件与Select2插件的冲突

你使用的select2是基于jQuery的DOM操作插件,它会直接修改原生select的DOM结构,和React的虚拟DOM更新机制冲突。React状态变更后,select2无法同步更新渲染结果,导致部门下拉框的选项显示滞后。

次要问题

  • 部门下拉框未设置value属性,不属于受控组件,状态与UI无法双向同步
  • 当selectedSbuId清空或接口返回空数据时,departmentData未被重置,会保留旧数据
  • 两个下拉框的id重复(都为inputGroupSelect01),引发DOM标识冲突

修复步骤

1. 解决Select2与React的冲突(二选一)

方案一:移除Select2,使用原生受控组件

如果不需要select2的特殊样式,直接去掉select2类名,改用原生select并保持受控状态:

// SBU下拉框
<select className="form-select" id="sbuSelect"
  onChange={(e) => setSelectedSbuId(e.target.value)}
  value={selectedSbuId}
>
  <option value="">Select SBU</option>
  {sbuData?.map((option) => (
    <option key={option.id} value={option.id}>{option.name}</option>
  ))}
</select>

// 部门下拉框
<select className="form-select" id="deptSelect"
  value={''}
>
  <option value="">Select Department</option>
  {departmentData?.map((option)=> (
    <option key={option.id} value={option.id}>{option.name + "-" + option.sbu_name}</option>
  ))}
</select>
方案二:保留Select2,手动同步状态与DOM

若必须使用select2,需通过ref绑定DOM节点,在状态变更时手动更新select2实例:

import React, { useEffect, useState, useRef } from "react";

const Employee = () => {
  const [selectedSbuId, setSelectedSbuId] = useState("");
  const [sbuData, setSbuData] = useState([]);
  const [departmentData, setDepartmentData] = useState([]);
  const sbuSelectRef = useRef(null);
  const deptSelectRef = useRef(null);

  useEffect(() => {
    fetchSbuData();
  }, []);

  // 初始化SBU的Select2实例并绑定事件
  useEffect(() => {
    if (!sbuSelectRef.current) return;
    const $select = $(sbuSelectRef.current);
    $select.select2();
    $select.on('select2:select', (e) => setSelectedSbuId(e.target.value));
    
    // 组件卸载时清理实例
    return () => {
      $select.off('select2:select').select2('destroy');
    };
  }, [sbuData]);

  useEffect(() => {
    fetchDepartmentData();
  }, [selectedSbuId]);

  // 部门数据更新后重新初始化Select2
  useEffect(() => {
    if (!deptSelectRef.current) return;
    const $select = $(deptSelectRef.current);
    $select.select2('destroy').select2();
    
    return () => {
      $select.select2('destroy');
    };
  }, [departmentData]);

  const fetchSbuData = async () => {
    try {
      const responseSbu = await axios.post(BASE_URL + '/api/sbu/get_sbu.php', { Name: "IMSL", ResponseType: "list" });
      setSbuData(responseSbu.data.success && responseSbu.data.resultList.length > 0 ? responseSbu.data.resultList : []);
    } catch (error) {
      console.error("Error fetching SBU data:", error);
      setSbuData([]);
    }
  };

  const fetchDepartmentData = async () => {
    try {
      let response;
      if (selectedSbuId) {
        response = await axios.post(BASE_URL + '/api/department/get_department.php', { SbuId: selectedSbuId, ResponseType: "detail" });
      } else {
        response = await axios.post(BASE_URL + '/api/department/get_department.php');
      }
      setDepartmentData(response.data.success && response.data.resultList.length > 0 ? response.data.resultList : []);
    } catch (error) {
      console.error("Error fetching department data:", error);
      setDepartmentData([]);
    }
  };

  return (
    <div>
      <Navbar />
      <div className="container-fluid">
        <div className="search-box">
          <div className="card">
            <div className="card-header">
              <h5 className="card-title">Card title</h5>
            </div>
            <div className="card-body">
              <div className="row">
                <div className="col-xl-3 col-lg-6">
                  <div className="input-group mb-3">
                    <span className="input-group-text">SBU</span>
                    <select 
                      ref={sbuSelectRef}
                      className="form-select select2" 
                      id="sbuSelect"
                      value={selectedSbuId}
                    >
                      <option value="">Select SBU</option>
                      {sbuData.map((option) => (
                        <option key={option.id} value={option.id}>{option.name}</option>
                      ))}
                    </select>
                  </div>
                </div>
                <div className="col-xl-3 col-lg-6">
                  <div className="input-group mb-3">
                    <span className="input-group-text">Department</span>
                    <select 
                      ref={deptSelectRef}
                      className="form-select select2" 
                      id="deptSelect"
                      value=""
                    >
                      <option value="">Select Department</option>
                      {departmentData.map((option)=> (
                        <option key={option.id} value={option.id}>{option.name + "-" + option.sbu_name}</option>
                      ))}
                    </select>
                  </div>
                </div>
                <div className="col-xl-3 col-lg-6">
                  <DesignationList/>
                </div>
                <div className="col-xl-3 col-lg-6">
                  <OfficeList/>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Employee;

2. 重置部门数据

在fetchDepartmentData中,无论接口返回空数据还是请求失败,都将departmentData重置为空数组,避免旧数据残留。

3. 修正重复ID

将两个下拉框的id修改为唯一值(如sbuSelect和deptSelect),避免DOM标识冲突。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 02:45:55