3rd dropdown is based off first 2 dropdown's selections but the 3rd's state is always 1 step behind

Viewed 27

I have the following code:

const [homeSelect, setHomeSelect] = useState('Home');
const [hedgeSelect, setHedgeSelect] = useState('Hedge');
const [symbolSelect, setSymbolSelect] = useState('');
const [similarSymbols, setSimilarSymbols] = useState([]);

const handleHome = (event) => {
    setHomeSelect(event.target.value);
    exchange_change();
  };

const handleHedge = (event) => {
    setHedgeSelect(event.target.value);
    exchange_change();
};

const handleSymbol = (event) => {
    setSymbolSelect(event.target.value);
};

const exchange_change = () => {
    setSimilarSymbols([]);
    //add symbols together from selected dropdowns
    var allSymbols = response.content.symbols[homeSelect] + response.content.symbols[hedgeSelect];
    console.log(allSymbols);

    //sort the symbols
    allSymbols = allSymbols.split(",");
    var sort_arr = allSymbols.sort();
    console.log(sort_arr);

    //find duplicates
    for(var i = 0; i < sort_arr.length-1; i++)
    {
      if(sort_arr[i + 1] == sort_arr[i]) { 
        setSimilarSymbols(similarSymbols => [...similarSymbols, sort_arr[i]]);
      }
    }
  }

<FormControl dense>
    <TextField
      id="standard-select-currency"
      select
      label="Home"
      className={classes.textField}
      value={homeSelect}
      onChange={handleHome}
      SelectProps={{
        native: true,
      }}
      helperText="Please select exchange"
    >
      <option>Home</option>
      {response ? response.content.exchanges.map((option) => (
        <option value={option}>
          {option}
        </option>
      )) : <option>Data is loading...</option>}
    </TextField>
  </FormControl>

  <FormControl dense>
    <TextField
      id="standard-select-currency"
      select
      label="Hedge"
      className={classes.textField}
      value={hedgeSelect}
      onChange={handleHedge}
      SelectProps={{
        native: true,
      }}
      helperText="Please select exchange"
    >
      <option>Hedge</option>
      {response ? response.content.exchanges.map((option) => (
        <option value={option}>
          {option}
        </option>
      )) : <option>Data is loading...</option>}
    </TextField>
  </FormControl>

  <FormControl dense>
    <TextField
      id="standard-select-currency"
      select
      label="Symbols"
      className={classes.textField}
      value={symbolSelect}
      onChange={handleSymbol}
      disabled={homeSelect == 'Home' || hedgeSelect == 'Hedge'}
      SelectProps={{
        native: true,
      }}
      helperText="Please select the exchanges"
    >
      <option>Symbol</option>
      {console.log(similarSymbols.map((element) => (element)))}
      {response ? similarSymbols.map((option) => (
        <option value={option}>
          {option}
        </option>
      )) : <option>Data is loading...</option>}
    </TextField>
  </FormControl>

After homeSelect and hedgeSelect are selected, 'similarSymbols' should be populated with the according data.

The issue is, 'similarSymbols' won't populate correctly until after homeSelect and hedgeSelect are selected one more time, for ex, select one dropdown and select another dropdown, and then select one of them one more time.

It seems as if the state is 1 step behind and I can't figure out how to make it work.

Any help is appreciated, thank you.

1 Answers

This is caused by the async of the setState, also the hook.

By the time exchange_change is called, the state is not yet set and the previous values are selected.

Try a useEffect to react to chagnes made to homeSelect and hedgeSelect:

const [homeSelect, setHomeSelect] = useState('Home');
const [hedgeSelect, setHedgeSelect] = useState('Hedge');
const [symbolSelect, setSymbolSelect] = useState('');
const [similarSymbols, setSimilarSymbols] = useState([]);


const handleHome = (event) => {
    setHomeSelect(event.target.value);
  };

const handleHedge = (event) => {
    setHedgeSelect(event.target.value);
};

const handleSymbol = (event) => {
    setSymbolSelect(event.target.value);
};
React.useEffect(exchange_change, [homeSelect, hedgeSelect]) // Here you listen to changes and update similarSymbols once changes are made.

const exchange_change = () => {
    setSimilarSymbols([]);
    //add symbols together from selected dropdowns
    var allSymbols = response.content.symbols[homeSelect] + response.content.symbols[hedgeSelect];
    console.log(allSymbols);

    //sort the symbols
    allSymbols = allSymbols.split(",");
    var sort_arr = allSymbols.sort();
    console.log(sort_arr);

    //find duplicates
    for(var i = 0; i < sort_arr.length-1; i++)
    {
      if(sort_arr[i + 1] == sort_arr[i]) { 
        setSimilarSymbols(similarSymbols => [...similarSymbols, sort_arr[i]]);
      }
    }
  }

<FormControl dense>
    <TextField
      id="standard-select-currency"
      select
      label="Home"
      className={classes.textField}
      value={homeSelect}
      onChange={handleHome}
      SelectProps={{
        native: true,
      }}
      helperText="Please select exchange"
    >
      <option>Home</option>
      {response ? response.content.exchanges.map((option) => (
        <option value={option}>
          {option}
        </option>
      )) : <option>Data is loading...</option>}
    </TextField>
  </FormControl>

  <FormControl dense>
    <TextField
      id="standard-select-currency"
      select
      label="Hedge"
      className={classes.textField}
      value={hedgeSelect}
      onChange={handleHedge}
      SelectProps={{
        native: true,
      }}
      helperText="Please select exchange"
    >
      <option>Hedge</option>
      {response ? response.content.exchanges.map((option) => (
        <option value={option}>
          {option}
        </option>
      )) : <option>Data is loading...</option>}
    </TextField>
  </FormControl>

  <FormControl dense>
    <TextField
      id="standard-select-currency"
      select
      label="Symbols"
      className={classes.textField}
      value={symbolSelect}
      onChange={handleSymbol}
      disabled={homeSelect == 'Home' || hedgeSelect == 'Hedge'}
      SelectProps={{
        native: true,
      }}
      helperText="Please select the exchanges"
    >
      <option>Symbol</option>
      {console.log(similarSymbols.map((element) => (element)))}
      {response ? similarSymbols.map((option) => (
        <option value={option}>
          {option}
        </option>
      )) : <option>Data is loading...</option>}
    </TextField>
  </FormControl>
Related