Home > Net >  React set default value from Select List after load
React set default value from Select List after load

Time:09-14

I have a form that has a select list of countries and a cascading select list of regions. When a new country is selected, the corresponding regions are loaded. I am trying to set the value for the region after the the select list loads as the first value in the list. On page render I am loading the country select list here:

useEffect(() => {
    fetchCountries();
  }, []);

When a new country is selected, I am triggering the corresponding regions to be loaded here:

useEffect(() => {
    fetchRegions();
    regionData.length && setInput({...input, ["region"]: regionData[0].value})
  }, [input["country"]]);

This is also where I am trying to set the default region value as the first item in the list, but the state is not being updated. Where have I gone wrong? More code below, with non-relevant code removed for brevity.

export default function Signup() {
  const initialInput: SignupRequest = {
    country: "US",
    region: ""
  };

  const initialErrors: ValidationError = {
    country: "",
    region: ""
  };

  const [input, setInput] = useState<SignupRequest>(initialInput);
  const [errors, setErrors] = useState<ValidationError>(initialErrors);
  const [countryData, setCountryData] = useState<SelectListModel["data"]>([]);
  const [countryLoaded, setCountryLoaded] = useState<boolean>(false);
  const [regionData, setRegionData] = useState<SelectListModel["data"]>([]);
  const [regionLoaded, setRegionLoaded] = useState<boolean>(false);

  useEffect(() => {
    fetchCountries();
  }, []);

  useEffect(() => {
    fetchRegions();
    regionData.length && setInput({...input, ["region"]: regionData[0].value})
  }, [input["country"]]);

  function handleSelectChange(event: React.ChangeEvent<HTMLSelectElement>) {
    const { name, value } = event.target;
    setInput({ ...input, [name]: value });
  }

  function handleBlur(event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) {
    const { name, value } = event.target;
    validateInput(name, value);
  }

  function validateInput(name: string, value: string | string[] | boolean) {
    let result = ValidationService.ValidateInput(name, value);
    setErrors({ ...errors, [name]: result });
  }


  function handleFormSubmit(event: React.FormEvent<HTMLFormElement>) {
    // process form
  }

  async function fetchCountries() {
    const response = await LocationService.getAllCountries();
    if (response?.ok) {
      const responseData = await response.json() as CountryResponse[];
      const countries = LocationService.maptoCountrySelectList(responseData);
      setCountryData(countries);
      setCountryLoaded(true);
    }
  }

  async function fetchRegions() {
    const response = await LocationService.getRegionsByCountryCode(input["country"]);
    if (response?.ok) {
      const responseData = await response.json() as RegionResponse[];
      const regions = LocationService.maptoRegionSelectList(responseData);
      setRegionData(regions);
      setRegionLoaded(true);
    }
  }

  return (
    <div className="account-content">
      <form onSubmit={handleFormSubmit}>
        <FormGroup addClass='form-group'>
          <Label label='country' title='Country' />
          {!countryLoaded 
            ? <LoadingSpinner text="Loading..." /> 
            : <SelectList data={countryData} name='country' value={input["country"]} addClass={countryLoaded && errors["country"] ? 'is-invalid' : ''} onChange={handleSelectChange} onBlur={handleBlur} />}
            <FormError message={errors["country"]} />
        </FormGroup>
        <FormGroup addClass='form-group'>
          <Label label='region' title='State/Region/Province' />
          {!regionLoaded 
            ? <LoadingSpinner text="Loading..." /> 
            : <SelectList data={regionData} name='region' value={input["region"]} addClass={regionLoaded && errors["region"] ? 'is-invalid' : ''} onChange={handleSelectChange} onBlur={handleBlur} />}
            <FormError message={errors["region"]} />
        </FormGroup>
        <Button type='submit' value='Sign Up' addClass='btn-mdBlue' />
      </form>
    </div>
  )
}

CodePudding user response:

You need to setInput for region inside your fetchRegions function because setState is not synchonous so when you setRegion, and right after you setInput, region is still empty, on next render region gets filled with your previous setRegion but useEffect has already been run

const fetchRegions = async () => {
    const regions = await // fetchsomething
    setRegions(regions);
    setInput({ ...input, region: regions[0].value });
}
  • Related