React三级联动下拉框(location/third_ip/fourth_ip)异常问题咨询
问题
数据库数据
[ { "ID": 3, "fourth_ip": "5", "location": "jp", "third_ip": "20" }, { "ID": 4, "fourth_ip": "123", "location": "jp", "third_ip": "21" }, { "ID": 5, "fourth_ip": "123", "location": "hk", "third_ip": "20" }, { "ID": 6, "fourth_ip": "200", "location": "sg", "third_ip": "11" }, { "ID": 7, "fourth_ip": "6", "location": "jp", "third_ip": "20" }, { "ID": 8, "fourth_ip": "200", "location": "sg", "third_ip": "12" } ]
需求
实现三个关联的Select组件,对应location、third_ip、fourth_ip:
- 选择
location为jp时,third_ip可选值为20、21 - 选择
third_ip为20时,fourth_ip可选值为5、6
问题现象
页面首次加载显示正常(location为jp,third_ip为20,fourth_ip为5),但切换location为hk后,third_ip显示为20,fourth_ip却为空值。
现有实现代码
useEffect(() => { // setEdgeData(data) const edgeLocation:never[] = Array.from(new Set(data.map((item: { location: any; }) => item.location))); setEdgeLocation(edgeLocation); // setRegion(edgeLocation[0]) if (!region) { setRegion(edgeLocation[0]); } console.log(region) }, [data]); useEffect(() => { setThirdIP("") const selectedThirdIP:never[] = Array.from(new Set( data .filter((item: { location: string; }) => item.location === region) .map((item: { third_ip: any; }) => item.third_ip))); console.log(selectedThirdIP) console.log(selectedThirdIP[0]) setSelectedThirdIP(selectedThirdIP) if (!thirdIP) { setThirdIP(selectedThirdIP[0]) } console.log('thirdip = '+thirdIP) }, [region]); console.log('thirdip = '+thirdIP) useEffect(() => { const selectedFourthIP:never[] = Array.from(new Set( data .filter((item: { location: string, third_ip: string }) => item.location === region && item.third_ip === thirdIP) .map((item: { fourth_ip: any; }) => item.fourth_ip))); console.log(selectedFourthIP) console.log(selectedFourthIP[0]) setSelectedFourthIP(selectedFourthIP) if (!fourthIP) { setFourthIP(selectedFourthIP[0]) } console.log(fourthIP) }, [region, thirdIP]); console.log(fourthIP) <select onChange={handleRegionChange} value={region}> {edgeLocation.map((location) => ( <option key={location} value={location}> {location} </option> ))} </select> </p> <select onChange={handleThirdIPChange} value={thirdIP}> {selectedThirdIP.map((thirdIP) => ( <option key={thirdIP} value={thirdIP}> {thirdIP} </option> ))} </select>. <select onChange={handleFourthIPChange} value={fourthIP}> {selectedFourthIP.map((fourthIP) => ( <option key={fourthIP} value={fourthIP}> {fourthIP} </option> ))} </select>
解决方法
问题根源
- 状态更新时序混乱:切换location时,先执行
setThirdIP("")让thirdIP变为空值,触发fourthIP的更新逻辑,但此时新的thirdIP可选列表还未设置,后续再设置setThirdIP(selectedThirdIP[0])时,fourthIP的更新逻辑不会重新触发(因为if (!fourthIP)的判断已经执行过)。 - 不必要的空值重置:先清空thirdIP再赋值的操作,会导致中间状态异常,破坏联动逻辑的同步性。
修复方案
- 移除
setThirdIP(""),避免中间状态为空。 - 切换location时,直接将thirdIP设置为新可选列表的第一个值,无需判断当前状态。
- fourthIP的更新逻辑中,直接设置为新可选列表的第一个值,确保每次region或thirdIP变化时都同步更新。
修改后的完整代码
import { useEffect, useState } from 'react'; // 假设data是传入的数据库数据 const IpSelector = ({ data }) => { const [region, setRegion] = useState(''); const [edgeLocation, setEdgeLocation] = useState([]); const [thirdIP, setThirdIP] = useState(''); const [selectedThirdIP, setSelectedThirdIP] = useState([]); const [fourthIP, setFourthIP] = useState(''); const [selectedFourthIP, setSelectedFourthIP] = useState([]); const handleRegionChange = (e) => { setRegion(e.target.value); }; const handleThirdIPChange = (e) => { setThirdIP(e.target.value); }; const handleFourthIPChange = (e) => { setFourthIP(e.target.value); }; useEffect(() => { const locations = Array.from(new Set(data.map(item => item.location))); setEdgeLocation(locations); if (!region) { setRegion(locations[0]); } }, [data]); useEffect(() => { const thirdIpList = Array.from(new Set( data.filter(item => item.location === region).map(item => item.third_ip) )); setSelectedThirdIP(thirdIpList); // 直接设置为新列表的第一个值,确保状态同步 setThirdIP(thirdIpList[0]); }, [region]); useEffect(() => { const fourthIpList = Array.from(new Set( data.filter(item => item.location === region && item.third_ip === thirdIP).map(item => item.fourth_ip) )); setSelectedFourthIP(fourthIpList); // 直接设置为新列表的第一个值,避免空值 setFourthIP(fourthIpList[0]); }, [region, thirdIP]); return ( <> <select onChange={handleRegionChange} value={region}> {edgeLocation.map((location) => ( <option key={location} value={location}> {location} </option> ))} </select> <select onChange={handleThirdIPChange} value={thirdIP}> {selectedThirdIP.map((ip) => ( <option key={ip} value={ip}> {ip} </option> ))} </select> <select onChange={handleFourthIPChange} value={fourthIP}> {selectedFourthIP.map((ip) => ( <option key={ip} value={ip}> {ip} </option> ))} </select> </> ); }; export default IpSelector;
额外优化说明
- 移除了冗余的
never[]类型标注,让TypeScript自动推断类型,减少潜在错误。 - 修正了渲染部分多余的
</p>标签,保持DOM结构整洁。 - 确保每次联动状态变化时,下级组件的选中值都自动同步为新可选列表的第一个值,彻底避免空值问题。
内容的提问来源于stack exchange,提问作者Dearteddy
相关产品推荐
相关产品推荐

