#javascript #android #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 }) =gt; { 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) =gt; { if (field === 'country') { updateCityPicker(); } const newState = { ...state, [field]: val }; setState(newState); const err = validate(newState, validator); if (err) { setErrors(err); } else { setErrors({}); } }; useEffect(() =gt; { const err = validate({}, validator); setErrors(err); getUserAddressDetails(); }, []); const getUserAddressDetails = async () =gt; { 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 = () =gt; { const suggestions = cities.filter((el) =gt; { var s = el[country]; return s; }); var list = []; suggestions.forEach((element) =gt; { element[country].forEach((cl) =gt; { var obj = {}; obj['label'] = cl; obj['value'] = cl; list.push(obj); }); }); setCities(list); }; const { city, country } = state; const navigateToRegistrationConfirmDetails = () =gt; { if (socialRegistration) { updateUser({ variables: { data: { city, country, }, }, }).then(async () =gt; { await AsyncStorage.removeItem('userBasicDetails'); await AsyncStorage.removeItem('userAddressDetails'); navigation.navigate('QuestionnaireIntroduction'); }); } else { navigation.navigate('RegistrationConfirmDetails', { userDetails: state }); } }; return ( lt;ImageBackground style={styles.imageBackground}gt; lt;View style={styles.regStepView}gt; lt;Text style={styles.regStep}gt; Address lt;Text style={styles.oneOutOfThree}gt; - 3/3lt;/Textgt; lt;/Textgt; lt;/Viewgt; lt;ScrollView showsVerticalScrollIndicator={false}gt; lt;KeyboardAvoidingView style={styles.inputsView}gt; lt;Dropdown mainContainerStyle={{ width: '100%', backgroundColor: 'white', marginTop: 5, }} textStyle={{ fontSize: verticalScale(14) }} value={country} onValueChange={(val) =gt; onChange('country', val)} testID="countryID" placeholder="eg. United Kingdom" items={countries2} checkDropdownErrors={false} error={errors.accountCurrency amp;amp; errors.accountCurrency[0]} showDropdownError={''} title="Which country do you currently live in?" /gt; lt;Dropdown mainContainerStyle={{ width: '100%', backgroundColor: 'white', marginTop: 5, }} textStyle={{ fontSize: verticalScale(14) }} value={city} onValueChange={(val) =gt; onChange('city', val)} testID="cityID" placeholder="eg. London" items={filteredCities} checkDropdownErrors={false} error={errors.city amp;amp; errors.city[0]} showDropdownError={''} title="Which city do you currently live in?" /gt; lt;/KeyboardAvoidingViewgt; lt;/ScrollViewgt; lt;Viewgt; lt;Button disabled={Object.keys(errors).length} styleContainer={{ marginBottom: scale(24) }} title="Next" onPressFunc={async () =gt; { await AsyncStorage.setItem( 'userAddressDetails', JSON.stringify(state), ) .then(() =gt; navigateToRegistrationConfirmDetails()) .catch((err) =gt; console.log({ err })); }} /gt; lt;/Viewgt; lt;/ImageBackgroundgt; ); }; export default RegistrationAddress;
Я также получаю предупреждение о настройке состояния компонента из другого компонента, что понятно, но я не знаю решения.
Любая помощь была бы чрезвычайно признательна. Извините, если это существующий вопрос — другие ответы не совсем меня устроили.
Ответ №1:
Проблема в том, что состояние установки асинхронно. Его заказ не гарантируется. Так что в вашем случае полезно сохранить одно состояние использования, так как вы уже используете словарь. Идея заключается в том, что при изменении страны список городов будет отфильтрован в соответствии со страной, в противном случае он пуст. Я обновил код, как показано ниже, попробуйте. Это должно сработать для вас.
Пояснение: Я ввел новый атрибут списка городов, который будет обновляться в случае изменения страны. Также для отображения выпадающего списка мы используем тот же атрибут 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 }) =gt; { 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) =gt; { let cityList = state.cityList; if (field === 'country') { cityList = cities.filter((el) =gt; { 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(() =gt; { const err = validate({}, validator); setErrors(err); getUserAddressDetails(); }, []); const getUserAddressDetails = async () =gt; { 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 = () =gt; { const suggestions = cities.filter((el) =gt; { var s = el[country]; return s; }); var list = []; suggestions.forEach((element) =gt; { element[country].forEach((cl) =gt; { var obj = {}; obj['label'] = cl; obj['value'] = cl; list.push(obj); }); }); setCities(list); }; const { city, country, cityList } = state; const navigateToRegistrationConfirmDetails = () =gt; { if (socialRegistration) { updateUser({ variables: { data: { city, country, }, }, }).then(async () =gt; { await AsyncStorage.removeItem('userBasicDetails'); await AsyncStorage.removeItem('userAddressDetails'); navigation.navigate('QuestionnaireIntroduction'); }); } else { navigation.navigate('RegistrationConfirmDetails', { userDetails: state }); } }; return ( lt;ImageBackground style={styles.imageBackground}gt; lt;View style={styles.regStepView}gt; lt;Text style={styles.regStep}gt; Address lt;Text style={styles.oneOutOfThree}gt; - 3/3lt;/Textgt; lt;/Textgt; lt;/Viewgt; lt;ScrollView showsVerticalScrollIndicator={false}gt; lt;KeyboardAvoidingView style={styles.inputsView}gt; lt;Dropdown mainContainerStyle={{ width: '100%', backgroundColor: 'white', marginTop: 5, }} textStyle={{ fontSize: verticalScale(14) }} value={country} onValueChange={(val) =gt; onChange('country', val)} testID="countryID" placeholder="eg. United Kingdom" items={countries2} checkDropdownErrors={false} error={errors.accountCurrency amp;amp; errors.accountCurrency[0]} showDropdownError={''} title="Which country do you currently live in?" /gt; lt;Dropdown mainContainerStyle={{ width: '100%', backgroundColor: 'white', marginTop: 5, }} textStyle={{ fontSize: verticalScale(14) }} value={city} onValueChange={(val) =gt; onChange('city', val)} testID="cityID" placeholder="eg. London" items={cityList} checkDropdownErrors={false} error={errors.city amp;amp; errors.city[0]} showDropdownError={''} title="Which city do you currently live in?" /gt; lt;/KeyboardAvoidingViewgt; lt;/ScrollViewgt; lt;Viewgt; lt;Button disabled={Object.keys(errors).length} styleContainer={{ marginBottom: scale(24) }} title="Next" onPressFunc={async () =gt; { await AsyncStorage.setItem( 'userAddressDetails', JSON.stringify(state), ) .then(() =gt; navigateToRegistrationConfirmDetails()) .catch((err) =gt; console.log({ err })); }} /gt; lt;/Viewgt; lt;/ImageBackgroundgt; ); }; export default RegistrationAddress;
Комментарии:
1. С небольшим редактированием (мой список должен быть отформатирован определенным образом для компонента выбора) это отлично работает. Спасибо.