【问题标题】:React Native state does not update immediately when state is changed状态更改时,React Native 状态不会立即更新
【发布时间】:2021-12-25 14:48:56
【问题描述】:

所以我对 React native 很陌生,我正在对现有应用程序进行更新。我有两个下拉列表:一个国家下拉列表和一个城市下拉列表(这些最初是文本字段)。设置国家/地区后,城市下拉列表将从 JSON 文件填充。所有这些都可以正常工作,除非您更改国家/地区时不会立即填充城市列表。如果您设置国家两次 - 它会设置上一次选择的城市。

这是页面组件中的相关代码:

[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([]);
  const onChange = (field, val) => {
    if (field === 'country') {
      updateCityPicker();
    }
    const newState = { ...state, [field]: val };
    setState(newState);
    const err = validate(newState, validator);
    if (err) {
      setErrors(err);
    } else {
      setErrors({});
    }
  };

  useEffect(() => {
    const err = validate({}, validator);
    setErrors(err);
    getUserAddressDetails();
  }, []);

  const getUserAddressDetails = async () => {
    const userAddressDetail = await AsyncStorage.getItem('userAddressDetails');
    const userAddressDetails = JSON.parse(userAddressDetail);
    const userAddressDetailsState = {
      ...state,
      city: userAddressDetails.city || '',
      country: userAddressDetails.country || '',
    };

    setState(userAddressDetailsState);
    const err = validate(userAddressDetailsState, validator);
    if (err) {
      setErrors(err);
    } else {
      setErrors({});
    }
  };
  const updateCityPicker = () => {
    const suggestions = cities.filter((el) => {
      var s = el[country];
      return s;
    });
    var list = [];
    suggestions.forEach((element) => {
      element[country].forEach((cl) => {
        var obj = {};
        obj['label'] = cl;
        obj['value'] = cl;
        list.push(obj);
      });
    });
    setCities(list);
  };

  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');
      });
    } 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;

我还收到有关从另一个组件中设置组件状态的警告,这是可以理解的,但我不知道解决方案。

任何帮助将不胜感激。抱歉,如果这是一个现有问题 - 其他答案对我来说不太适用。

【问题讨论】:

    标签: javascript android react-native


    【解决方案1】:

    问题是 setState 是异步的。不保证其顺序。因此,在您的情况下,最好保留单一的 useState,因为您已经在使用字典。想法是,当您更改国家/地区时,cityList 将根据国家/地区进行过滤,否则为空。我已经更新了如下代码,试试吧。它应该适合你。

    说明:我引入了新的 cityList 属性,如果国家/地区发生变化,该属性将被更新。为了显示下拉列表,我们使用相同的 cityList 属性作为城市下拉列表。

    [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: '',
        cityList: [],
        personalDetails: personalDetails,
      });
      const [errors, setErrors] = useState({});
      const onChange = (field, val) => {
          let cityList = state.cityList;
        if (field === 'country') {
            cityList =  cities.filter((el) => {
                var s = el[country];
                return s;
              });
        }
        const newState = { ...state, [field]: val, cityList };
        setState(newState);
        const err = validate(newState, validator);
        if (err) {
          setErrors(err);
        } else {
          setErrors({});
        }
      };
    
      useEffect(() => {
        const err = validate({}, validator);
        setErrors(err);
        getUserAddressDetails();
      }, []);
    
      const getUserAddressDetails = async () => {
        const userAddressDetail = await AsyncStorage.getItem('userAddressDetails');
        const userAddressDetails = JSON.parse(userAddressDetail);
        const userAddressDetailsState = {
          ...state,
          city: userAddressDetails.city || '',
          country: userAddressDetails.country || '',
        };
    
        setState(userAddressDetailsState);
        const err = validate(userAddressDetailsState, validator);
        if (err) {
          setErrors(err);
        } else {
          setErrors({});
        }
      };
      const updateCityPicker = () => {
        const suggestions = cities.filter((el) => {
          var s = el[country];
          return s;
        });
        var list = [];
        suggestions.forEach((element) => {
          element[country].forEach((cl) => {
            var obj = {};
            obj['label'] = cl;
            obj['value'] = cl;
            list.push(obj);
          });
        });
        setCities(list);
      };
    
      const { city, country, cityList } = state;
    
      const navigateToRegistrationConfirmDetails = () => {
        if (socialRegistration) {
          updateUser({
            variables: {
              data: {
                city,
                country,
              },
            },
          }).then(async () => {
            await AsyncStorage.removeItem('userBasicDetails');
            await AsyncStorage.removeItem('userAddressDetails');
            navigation.navigate('QuestionnaireIntroduction');
          });
        } 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={cityList}
                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;
    

    【讨论】:

    • 通过一些编辑(我的列表必须为选择器组件以特定方式格式化),效果很好。谢谢。
    猜你喜欢
    • 2020-05-12
    • 2021-11-07
    • 2018-08-05
    • 2021-05-20
    • 1970-01-01
    • 1970-01-01
    • 2020-08-22
    相关资源
    最近更新 更多