You need to enable JavaScript to run this app. React开发三级联动地址表单时useState状态不更新返回undefined求助
优惠活动
大模型
产品
解决方案
定价
更多

React开发三级联动地址表单时useState状态不更新返回undefined求助

问题根因

  • country初始值为空字符串,首次渲染访问country.name直接触发undefined报错
  • React的setState是异步更新,调用后同步读取状态会拿到更新前的旧值
  • setFirstCountry的useCallback未添加依赖,始终读取初始的空国家列表
  • 国家选择器值绑定不匹配:Select的value绑定的是国家名称,MenuItem的value传的是国家短码,两边类型不一致导致选择异常
  • 调用setCountries后立即执行初始化国家逻辑,此时countries状态还未更新,无法拿到最新数据

修复后完整代码

import React,{ useEffect , useState , useCallback } from 'react';
import { InputLabel, Select , MenuItem , Grid , Typography } from '@material-ui/core';
import { useForm, FormProvider } from 'react-hook-form';
import CityHandler from 'countrycitystatejson';

export const TempAddresForm = () => 
{
    const methods = useForm();
    const [countries, setCountries] = useState([]);
    const [countryCode, setCountryCode] = useState('');
    // 初始值改为null,避免访问name报错
    const [country, setCountry] = useState(null);
    const [states, setStates] = useState([]);
    const [stateName, setStateName] = useState('');
    const [cities, setCities] = useState([]);
    const [city, setCity] = useState('');

    const fetchCountries = () => {
        const countryList = CityHandler.getCountries();
        setCountries(countryList);
        // 直接用拿到的列表初始化,不用等state更新
        setFirstCountry(countryList);
    };

    const countryChangeHandler = (event) => {
        const tempCountryCode = event.target.value;
        const currentCountry = CityHandler.getCountryByShort(tempCountryCode);
        setCountry(currentCountry);
        setCountryCode(tempCountryCode);
        // 切换国家后清空省和市的选择
        setStateName('');
        setCities([]);
        setCity('');
        fetchStates(tempCountryCode);
    }

    const fetchStates = (code) => 
    {
        const stateList = CityHandler.getStatesByShort(code);
        setStates(stateList);
        // 可选:自动选中第一个省份
        if(stateList.length > 0) {
            setStateName(stateList[0]);
            fetchCities(code, stateList[0]);
        }
    }

    const stateChangeHandler = (e) => 
    {
        const tempState = e.target.value;
        setStateName(tempState);
        fetchCities(countryCode, tempState);
        setCity('');
    }

    // 直接传参数避免依赖旧state
    const fetchCities = (code, stateName) => {
        const tempCities = CityHandler.getCities(code, stateName);
        setCities(tempCities);
        // 可选:自动选中第一个城市
        if(tempCities.length > 0) {
            setCity(tempCities[0]);
        }
    };

    const cityChangeHandler = (event) => 
    {
        const tempCity = event.target.value;
        setCity(tempCity);
    }

    const setFirstCountry = useCallback( (countryList) => 
    {
        if(countryList && countryList.length > 0)
        {
            const firstCountry = countryList[0];
            setCountry(firstCountry);
            setCountryCode(firstCountry.shortName);
            fetchStates(firstCountry.shortName);
        }
    }, []);

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

    return (
        <>
            <Typography variant="h6" gutterBottom>Shipping Address</Typography>
            <FormProvider {...methods}>
                <form onSubmit={() => {}}>
                    <Grid container spacing={3}>
                        
                        <Grid item xs={12} sm={6}>
                            <InputLabel>Country</InputLabel>
                            {/* value改为绑定countryCode和MenuItem对应 */}
                            <Select value={countryCode} fullWidth onChange={countryChangeHandler}>
                                {countries.map((countryLoop) => (
                                    <MenuItem key={countryLoop.shortName} id={countryLoop.shortName} value={countryLoop.shortName}>
                                        {countryLoop.name}
                                    </MenuItem>
                                ))}
                                
                            </Select>
                        </Grid>
                        <Grid item xs={12} sm={6}>
                            <InputLabel>State</InputLabel>
                            <Select value={stateName} fullWidth onChange={stateChangeHandler}>
                                {states.map((state, index) => {
                                    return(
                                        <MenuItem key={index} id={state} value={state}>
                                            {state}
                                        </MenuItem>
                                    );
                                })}
                                
                            </Select>
                        </Grid>
                        <Grid item xs={12} sm={6}>
                            <InputLabel>City</InputLabel>
                            <Select value={city} fullWidth onChange={cityChangeHandler}>
                                {cities.map((city, index) => {
                                    return(
                                        <MenuItem key={index} id={city} value={city}>
                                            {city}
                                        </MenuItem>
                                    );
                                })}
                                
                            </Select>
                        </Grid>
                        
                    </Grid>
                    
                </form>
            </FormProvider>
        </>
    )    
}

export default TempAddresForm;

关键修改点

  • 修正country初始值为null,避免首次访问属性报错
  • 修正国家选择器的值绑定,Select的value改为绑定countryCode,和MenuItem的value值匹配
  • 调整初始化逻辑,直接使用返回的国家列表进行初始化,不需要等待state异步更新
  • 切换国家时同步清空省份、城市的已选值,避免旧值残留
  • 给fetchCities增加国家短码参数,避免依赖旧的state值导致拉取错误
  • 可选增加了自动选中首个省份、首个城市的逻辑,可根据需求删除

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 09:06:03