React中country-state-city库州城级联实现求助及保加利亚语API咨询
问题解决:React中实现州-城市联动选择 + 保加利亚语地理数据方案
一、修复州-城市联动逻辑的问题
你的代码核心问题是未根据选中的州动态获取对应城市,同时状态初始化存在错误。以下是修正后的完整代码:
import FormControl from '@mui/material/FormControl'; import InputLabel from '@mui/material/InputLabel'; import Select from '@mui/material/Select'; import TextField from '@mui/material/TextField'; import MenuItem from '@mui/material/MenuItem'; import { useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import PriceSidebar from './PriceSidebar'; import Stepper from './Stepper'; import { useSnackbar } from 'notistack'; import { saveShippingInfo } from '../../actions/cartAction'; import { useNavigate } from 'react-router-dom'; import MetaData from '../Layouts/MetaData'; import { Country, State, City } from 'country-state-city'; import { ICountry, IState, ICity } from 'country-state-city' const Shipping = () => { const countryCode = 'BG'; const country = Country.getCountryByCode(countryCode); const allStates = State.getStatesOfCountry(country.isoCode); const dispatch = useDispatch(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); const { cartItems } = useSelector((state) => state.cart); const { shippingInfo } = useSelector((state) => state.cart); const [address, setAddress] = useState(shippingInfo.address); // 初始化选中第一个州的名称,优先用已有配送信息中的州 const [selectedState, setSelectedState] = useState(shippingInfo.state || allStates[0]?.name || ''); const [cities, setCities] = useState<ICity[]>([]); const [selectedCity, setSelectedCity] = useState(shippingInfo.city || ''); const [pincode, setPincode] = useState(shippingInfo.pincode); const [phoneNo, setPhoneNo] = useState(shippingInfo.phoneNo); // 监听选中州变化,动态获取对应城市 useEffect(() => { if (!selectedState) return; const targetState = allStates.find(s => s.name === selectedState); if (targetState) { const stateCities = City.getCitiesOfState(countryCode, targetState.isoCode); setCities(stateCities); // 若之前选中的城市不在当前州列表中,重置城市选择 if (!stateCities.some(c => c.name === selectedCity)) { setSelectedCity(''); } } }, [selectedState, allStates, selectedCity]); // 页面加载时默认加载第一个州的城市 useEffect(() => { if (allStates.length > 0) { const defaultState = allStates[0]; setSelectedState(defaultState.name); setCities(City.getCitiesOfState(countryCode, defaultState.isoCode)); } }, [allStates, countryCode]); const shippingSubmit = (e) => { e.preventDefault(); if (phoneNo.length < 10 || phoneNo.length > 10) { enqueueSnackbar("Invalid Phone Number", { variant: "error" }); return; } dispatch(saveShippingInfo({ address, city: selectedCity, state: selectedState, pincode, phoneNo })); navigate("/order/confirm"); } return ( <> <MetaData title="Flipkart: Shipping Details" /> <main className="w-full mt-20"> <div className="flex flex-col sm:flex-row gap-3.5 w-full sm:w-11/12 mt-0 sm:mt-4 m-auto sm:mb-7 overflow-hidden"> <div className="flex-1"> <Stepper activeStep={1}> <div className="w-full bg-white"> <form onSubmit={shippingSubmit} autoComplete="off" className="flex flex-col justify-start gap-3 w-full sm:w-3/4 mx-1 sm:mx-8 my-4"> <TextField value={address} onChange={(e) => setAddress(e.target.value)} fullWidth label="Address" variant="outlined" required /> <div className="flex gap-6"> <TextField value={pincode} onChange={(e) => setPincode(e.target.value)} type="number" label="Pincode" fullWidth variant="outlined" required /> <TextField value={phoneNo} onChange={(e) => setPhoneNo(e.target.value)} type="number" label="Phone No" fullWidth variant="outlined" required /> </div> <div className="flex gap-6"> <FormControl fullWidth > <InputLabel id="state-select">State</InputLabel> <Select labelId="state-select" id="state-select" value={selectedState} label="State" onChange={(e) => setSelectedState(e.target.value)} required > {allStates.map((state) => ( <MenuItem key={state.isoCode} value={state.name}>{state.name}</MenuItem> ))} </Select> </FormControl> <FormControl fullWidth > <InputLabel id="city-select">City</InputLabel> <Select labelId="city-select" id="city-select" value={selectedCity} label="City" onChange={(e) => setSelectedCity(e.target.value)} required disabled={!cities.length} > {cities.map((city) => ( <MenuItem key={city.id} value={city.name}>{city.name}</MenuItem> ))} </Select> </FormControl> </div> <button type="submit" className="bg-primary-orange w-full sm:w-1/3 my-2 py-3.5 text-sm font-medium text-white shadow hover:shadow-lg rounded-sm uppercase outline-none">save and deliver here</button> </form> </div> </Stepper> </div> <PriceSidebar cartItems={cartItems} /> </div> </main> </> ); }; export default Shipping;
关键修复点:
- 将原
state变量重命名为selectedState,避免和React内置state关键字冲突 - 用
useState维护当前选中州的城市列表cities,替代无效的全量遍历逻辑 - 添加两个
useEffect:- 页面加载时自动加载第一个州的城市
- 监听选中州的变化,动态拉取对应城市,并重置不匹配的城市选择
- 修正状态初始化逻辑,确保和下拉选项的
value(州名称)匹配 - 给城市选择框添加
disabled属性,无城市数据时禁用
二、保加利亚语城市及市政信息方案
如果需要保加利亚语的地理数据,可参考以下方案:
- 扩展
country-state-city库:查看该库的数据源文件,若支持多语言标签,可自行添加保加利亚语的州/城市名称 - 本地JSON数据集:从保加利亚官方统计机构获取公开地理数据,整理为本地JSON文件引入项目,直接使用保加利亚语名称
- 多语言地理库:选择支持保加利亚语本地化的前端地理数据库,直接引入使用
内容的提问来源于stack exchange,提问作者Maxi Kravaykov
相关产品推荐
相关产品推荐

