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
相关产品推荐
相关产品推荐

