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

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:
    1. 页面加载时自动加载第一个州的城市
    2. 监听选中州的变化,动态拉取对应城市,并重置不匹配的城市选择
  • 修正状态初始化逻辑,确保和下拉选项的value(州名称)匹配
  • 给城市选择框添加disabled属性,无城市数据时禁用

二、保加利亚语城市及市政信息方案

如果需要保加利亚语的地理数据,可参考以下方案:

  1. 扩展country-state-city库:查看该库的数据源文件,若支持多语言标签,可自行添加保加利亚语的州/城市名称
  2. 本地JSON数据集:从保加利亚官方统计机构获取公开地理数据,整理为本地JSON文件引入项目,直接使用保加利亚语名称
  3. 多语言地理库:选择支持保加利亚语本地化的前端地理数据库,直接引入使用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 12:55:22