#javascript #reactjs #react-hooks
Вопрос:
Я новичок в Reactjs, я пытаюсь создать адресную форму с 3 вариантами выбора ( страна, штат, город). Я использовал React hoc, поэтому при первой загрузке страницы он получит список стран для выбора страны, после этого, когда пользователь выберет страну, он получит список штатов для выбора штата, а затем, когда пользователь выберет штат, он получит список городов для выбора города моя проблема с состоянием Я сохраняю значение пользователя, выбранного в состоянии, но оно не обновило значение в состоянии во многих местах, я продолжаю получать «неопределенное», например, при загрузке страницы я получаю список стран в виде массива, и я получаю первую страну в списке в качестве элемента выбора по умолчанию в выборе страны, но я все еще получаю «неопределенное», Я перепробовал много способов, но все равно получаю тот же результат, и ниже приведен мой код
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('');
const [country, setCountry] = useState('');
const [states, setStates] = useState([]);
const [stateName, setStateName] = useState('');
const [cities, setCities] = useState([]);
const [city, setCity] = useState('');
const fetchCounters = () => {
setCountries(CityHandler.getCountries());
setFirstCountry();
};
const countryChangeHandler = (event) => {
let tempCountryCode = event.target.value;
setCountry(CityHandler.getCountryByShort(tempCountryCode));
setCountryCode(tempCountryCode);
fetchStates(tempCountryCode);
setCities([]);
}
const fetchStates = (countryCode) =>
{
setStates(CityHandler.getStatesByShort(countryCode));
}
const stateChangeHandler = (even) =>
{
let tempState = even.target.value;
setStateName(even.target.value);
fetchCities(tempState);
}
const fetchCities = (stateName) => {
let tempCities = CityHandler.getCities(countryCode, stateName);
setCities(tempCities);
};
const cityChangeHandler = (event) =>
{
let tempCity = event.target.value;
setCity(tempCity);
console.log("Temp City Name : " tempCity)
console.log("City Name : " city)
}
const setFirstCountry = useCallback( () =>
{
if(countries)
{
let firstCountry = CityHandler.getCountries()[0];
console.log ("[setFirstCountry] : First Country " JSON.stringify(firstCountry.name));
setCountry(firstCountry);
console.log ("[setFirstCountry] : Country name " JSON.stringify(country.name));
}
}, []);
useEffect(() => {
fetchCounters();
//setFirstCountry();
}, []);
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>
<Select value={country.name} fullWidth onChange={(event) => {countryChangeHandler(event)}}>
{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={(event) => {stateChangeHandler(event)}}>
{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={(event) => {cityChangeHandler(event)}}>
{cities.map((city, index) => {
return(
<MenuItem key={index} id={city} value={city}>
{city}
</MenuItem>
);
})}
</Select>
</Grid>
</Grid>
</form>
</FormProvider>
</>
)
}
export default TempAddresForm;
если бы кто — нибудь мог помочь мне с этим
*Примечание: Я использую этот пакет, чтобы получить список стран countrycitystatejson
Комментарии:
1. ваша проблема на начальном этапе (1-я загрузка),
countries = []
тогда вашеfirstCountry is undefined
право ? Я предполагаю, что ваше условие здесьif(countries)
означает, что страны правдивы или нет, чтобы проверить, пусты страны или нет, вы должны использоватьif(countries.length === 0)
2. спасибо за ваш ответ значения списка стран загружаются нормально для выбора, но когда я пытаюсь получить значение из состояния, чтобы использовать его «useState», как первая страна в массиве, я получил «неопределенное», и когда я устанавливаю новое значение, но когда я называю его, похоже, что значения нет или возвращает старое значение
Ответ №1:
country
на самом деле всегда обновляется, но вы регистрируете его в useCallback
крючке и не добавляете country
в его массив зависимостей. Таким образом, он захватывает только начальное значение country
, которое является пустой строкой ""
и JSON.stringify("".name)
не определено. Если вы добавите country
в массив зависимостей useCallback
, вы увидите, что он обновляется.
console.log( JSON.stringify("".name) )
Вам не нужно использовать useCallback
здесь. Прочитайте эту статью, чтобы понять, где и когда использовать useCallback
и useMemo
Основная проблема заключается в том, что вы сопоставляете свою страну Select
country.name
, но ваши выбранные параметры имеют country.shortName
значение — попробуйте изменить Select
значение на country.shortName
.
Кроме того, у вас слишком много переменных состояния, которые взаимозависимы друг от друга. Здесь перемещение всех переменных состояния в один объект состояния немного упростит обработку.
Например, Как показано ниже
{
countries: [...],
states: [...],
cities: [...],
stateName: "..",
...
...
}
countries
всегда является постоянным amp; states
, cities
являются просто производными значениями. Таким образом, вашему фактическому состоянию нужны только эти 3 значения countryShortCode, stateName and city
.
Вот фрагмент со всеми вышеупомянутыми изменениями
import React, { useState } from "react";
import {
InputLabel,
Select,
MenuItem,
Grid,
Typography
} from "@material-ui/core";
import { useForm, FormProvider } from "react-hook-form";
import CityHandler from "countrycitystatejson";
// This countriesList doesn't change so it can just be a constant outside your component
const countriesList = CityHandler.getCountries();
// Your initial state
const initialState = {
countryShortName: countriesList[0].shortName,
stateName: "",
city: ""
};
const TempAddresForm = () => {
const methods = useForm();
const [state, setState] = useState(initialState);
const changeCountry = (e) => {
setState((prevState) => ({
...prevState,
countryShortName: e.target.value,
stateName: "",
city: ""
}));
};
const changeState = (e) => {
setState((prevState) => ({
...prevState,
stateName: e.target.value,
city: ""
}));
};
const changeCity = (e) => {
setState((prevState) => ({
...prevState,
city: e.target.value
}));
};
// Derive both states and cities here ( No need to store it in state :) )
const states = CityHandler.getStatesByShort(state.countryShortName);
const cities = state.stateName
? CityHandler.getCities(state.countryShortName, state.stateName)
: [];
return (
<>
<Typography variant="h6" gutterBottom>
Shipping Address
</Typography>
<FormProvider {...methods}>
<form onSubmit={() => console.log("Submitted")}>
<Grid container spacing={3}>
<Grid item xs={12} sm={6}>
<InputLabel>Country</InputLabel>
<Select
value={state.countryShortName || ""}
fullWidth
onChange={changeCountry}
>
{countriesList.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={state.stateName} fullWidth onChange={changeState}>
{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={state.city || ""} fullWidth onChange={changeCity}>
{cities.map((city, index) => {
return (
<MenuItem key={index} id={city} value={city}>
{city}
</MenuItem>
);
})}
</Select>
</Grid>
</Grid>
</form>
</FormProvider>
</>
);
};
export default TempAddresForm;
Прокомментируйте, если вам нужно понять что-нибудь еще
Комментарии:
1. Вот ссылка , если вы хотите попробовать эти изменения.