Я использую useState hock для хранения значения, но оно не обновлялось

#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. Вот ссылка , если вы хотите попробовать эти изменения.