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

React中如何绑定依赖API数据的联动下拉框默认值?

问题分析与解决方案

核心问题

编辑场景下,formik.values已包含有效的countryId、stateId、cityId,但现有逻辑存在以下缺陷导致State/City下拉无法绑定默认值:

  • 依赖previousCountryId/previousStateId的判断阻止了初始化时自动加载对应层级的选项列表
  • 编辑模式下的useEffect错误地将stateId重置为0,覆盖了从API获取的初始值
  • 重复的useEffect导致逻辑冲突,状态更新混乱

修复后的代码实现

import { useState, useEffect } from 'react';
import { FormikProps } from 'formik';

type TourFormValues = {
  countryId: number;
  stateId: number;
  cityId: number;
};

const TourForm = ({ formik, tourId }: { formik: FormikProps<TourFormValues>; tourId?: number }) => {
  const [countries, setCountries] = useState<any[]>([]);
  const [states, setStates] = useState<any[]>([]);
  const [cities, setCities] = useState<any[]>([]);

  // 获取国家列表
  const bindCountries = () => {
    getAllCountries()
      .then((res: any) => {
        setCountries(res.data.status ? res.data.data : []);
      })
      .catch(() => setCountries([]));
  };

  // 根据国家ID获取州列表
  const bindStates = (countryId: number) => {
    if (!countryId) return setStates([]);
    getStatesByCountry(countryId)
      .then((res: any) => setStates(res.data.data || []))
      .catch(err => console.log(err));
  };

  // 根据州ID获取城市列表
  const bindCities = (stateId: number) => {
    if (!stateId) return setCities([]);
    getCitiesByState(stateId)
      .then((res: any) => setCities(res.data.data || []))
      .catch(err => console.log(err));
  };

  // 初始化加载数据
  useEffect(() => {
    bindCountries();
    // 编辑模式下,直接根据初始值加载对应州和城市
    if (tourId && formik.values.countryId) {
      bindStates(formik.values.countryId);
      if (formik.values.stateId) {
        bindCities(formik.values.stateId);
      }
    }
  }, [tourId, formik.values.countryId, formik.values.stateId]);

  // 国家变化时更新州和城市
  useEffect(() => {
    if (formik.values.countryId) {
      bindStates(formik.values.countryId);
      // 仅新增模式下重置州和城市选中值
      if (!tourId) {
        formik.setFieldValue('stateId', 0);
        formik.setFieldValue('cityId', 0);
        setCities([]);
      }
    } else {
      setStates([]);
      setCities([]);
    }
  }, [formik.values.countryId, tourId]);

  // 州变化时更新城市
  useEffect(() => {
    if (formik.values.stateId) {
      bindCities(formik.values.stateId);
      // 仅新增模式下重置城市选中值
      if (!tourId) {
        formik.setFieldValue('cityId', 0);
      }
    } else {
      setCities([]);
    }
  }, [formik.values.stateId, tourId]);

  return (
    <>
      <FieldSelect
        className='col-lg-6'
        isRequired
        label='Country'
        name='countryId'
        formik={formik}
      >
        <option value={0}>Select Country</option>
        {countries.map((item) => (
          <option key={item.id} value={item.id}>
            {item.name}
          </option>
        ))}
      </FieldSelect>

      <FieldSelect
        className='col-lg-6'
        isRequired
        label='State'
        name='stateId'
        formik={formik}
      >
        <option value={0}>Select State</option>
        {states.map((item) => (
          <option key={item.id} value={item.id}>
            {item.name}
          </option>
        ))}
      </FieldSelect>

      <FieldSelect
        className='col-lg-6'
        isRequired
        label='City'
        name='cityId'
        formik={formik}
      >
        <option value={0}>Select City</option>
        {cities.map((item) => (
          <option key={item.id} value={item.id}>
            {item.name}
          </option>
        ))}
      </FieldSelect>
    </>
  );
};

关键修复点

  • 移除冗余状态:删除previousCountryId、previousStateId和无用ref,简化依赖逻辑
  • 初始化逻辑优化:编辑模式下直接根据Formik初始值加载对应层级的选项列表
  • 避免值覆盖:仅在新增模式(无tourId)下重置stateId和cityId,编辑模式保留初始值
  • 下拉框绑定简化:FieldSelect通过name属性与Formik自动绑定,无需手动设置selected或defaultValue

内容的提问来源于stack exchange,提问作者Ashwani Bhardwaj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 09:56:21