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

React Native修改state未立即更新,切换国家时城市下拉框数据滞后如何解决?

问题根因分析
  • 城市下拉列表延迟更新:React 中 setState 为异步更新,你在 onChange 回调中先执行 updateCityPicker,此时 state.country 还未被更新为最新选中的国家,过滤城市时使用的还是上一次的旧值,因此每次更新的都是上一次选中国家对应的城市列表,需要两次切换才能看到正确结果。
  • 跨组件setState告警:异步函数getUserAddressDetails、updateUser的then回调执行时,可能组件已经被卸载,此时仍然调用setState就会触发该告警;同时空依赖useEffect中引用的state是初始闭包值,也会导致状态更新异常。
修复代码
[imports all in place]

const countries2 = require('../../common/constants/countries2.json');
const cities = require('../../common/constants/cities.json');
const validator = {
  city: {
    required: true,
    string: true,
  },
  country: {
    required: true,
    string: true,
  },
};
const RegistrationAddress = ({ route }) => {
  const navigation = useNavigation();
  const { personalDetails, socialRegistration } = route.params || {};
  const [updateUser] = useMutation(UPDATE_PROFILE, {
    fetchPolicy: 'no-cache',
  });

  const [state, setState] = useState({
    city: '',
    country: '',
    personalDetails: personalDetails,
  });
  const [errors, setErrors] = useState({});
  const [filteredCities, setCities] = useState([]);

  // 监听country变化自动更新城市列表
  useEffect(() => {
    const currentCountry = state.country;
    if (!currentCountry) {
      setCities([]);
      return;
    }
    const suggestions = cities.filter((el) => el[currentCountry]);
    const list = [];
    suggestions.forEach((element) => {
      element[currentCountry].forEach((cl) => {
        list.push({ label: cl, value: cl });
      });
    });
    setCities(list);
    // 切换国家后清空已选城市,避免状态不一致
    setState(prev => ({...prev, city: ''}));
  }, [state.country]);

  // 新增挂载状态标记,避免组件卸载后更新state触发告警
  useEffect(() => {
    let isMounted = true;
    const err = validate({}, validator);
    if (isMounted) setErrors(err);
    getUserAddressDetails(isMounted);

    return () => {
      isMounted = false;
    }
  }, []);

  const onChange = (field, val) => {
    const newState = { ...state, [field]: val };
    setState(newState);
    const err = validate(newState, validator);
    if (err) {
      setErrors(err);
    } else {
      setErrors({});
    }
  };

  const getUserAddressDetails = async (isMounted) => {
    const userAddressDetail = await AsyncStorage.getItem('userAddressDetails');
    const userAddressDetails = JSON.parse(userAddressDetail) || {};
    // 改用函数式更新,避免闭包读取旧state的问题
    setState(prev => ({
      ...prev,
      city: userAddressDetails.city || '',
      country: userAddressDetails.country || '',
    }));
    if (!isMounted) return;
    const err = validate({
      city: userAddressDetails.city || '',
      country: userAddressDetails.country || '',
    }, validator);
    if (err) {
      setErrors(err);
    } else {
      setErrors({});
    }
  };

  const { city, country } = state;

  const navigateToRegistrationConfirmDetails = () => {
    if (socialRegistration) {
      updateUser({
        variables: {
          data: {
            city,
            country,
          },
        },
      }).then(async () => {
        await AsyncStorage.removeItem('userBasicDetails');
        await AsyncStorage.removeItem('userAddressDetails');
        navigation.navigate('QuestionnaireIntroduction');
      }).catch(err => console.log({err}));
    } else {
      navigation.navigate('RegistrationConfirmDetails', { userDetails: state });
    }
  };

  return (
    <ImageBackground style={styles.imageBackground}>
      <View style={styles.regStepView}>
        <Text style={styles.regStep}>
          Address
          <Text style={styles.oneOutOfThree}> - 3/3</Text>
        </Text>
      </View>
      <ScrollView showsVerticalScrollIndicator={false}>
        <KeyboardAvoidingView style={styles.inputsView}>
          <Dropdown
            mainContainerStyle={{
              width: '100%',
              backgroundColor: 'white',
              marginTop: 5,
            }}
            textStyle={{ fontSize: verticalScale(14) }}
            value={country}
            onValueChange={(val) => onChange('country', val)}
            testID="countryID"
            placeholder="eg. United Kingdom"
            items={countries2}
            checkDropdownErrors={false}
            error={errors.accountCurrency && errors.accountCurrency[0]}
            showDropdownError={''}
            title="Which country do you currently live in?"
          />
          <Dropdown
            mainContainerStyle={{
              width: '100%',
              backgroundColor: 'white',
              marginTop: 5,
            }}
            textStyle={{ fontSize: verticalScale(14) }}
            value={city}
            onValueChange={(val) => onChange('city', val)}
            testID="cityID"
            placeholder="eg. London"
            items={filteredCities}
            checkDropdownErrors={false}
            error={errors.city && errors.city[0]}
            showDropdownError={''}
            title="Which city do you currently live in?"
          />
        </KeyboardAvoidingView>
      </ScrollView>
      <View>
        <Button
          disabled={Object.keys(errors).length}
          styleContainer={{ marginBottom: scale(24) }}
          title="Next"
          onPressFunc={async () => {
            await AsyncStorage.setItem(
              'userAddressDetails',
              JSON.stringify(state),
            )
              .then(() => navigateToRegistrationConfirmDetails())
              .catch((err) => console.log({ err }));
          }}
        />
      </View>
    </ImageBackground>
  );
};

export default RegistrationAddress;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 23:45:02