0

I have a function to disable a dropdown until selections have been made in 2 other dropdowns. It is not enabling the dropdown properly I believe due to some asynchronous issues with useState.

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

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 = () => {
  if (homeSelect != 'Home' && hedgeSelect != 'Hedge'){
    //enable the symbol dropdown
      setDisabled(false);
  } else {
      //disable the select exchanges dropdown
      setDisabled(true);
  }
}
<FormControl dense>
  <TextField
    id="standard-select-currency"
    select
    label="Home"
    className={classes.textField}
    value={homeSelect}
    onChange={handleHome}
    SelectProps={{
      native: true,
    }}
    helperText="Please select exchange"
  >
    {home.map((option) => (
      <option key={option.value} value={option.value}>
        {option.label}
      </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"
  >
    {hedge.map((option) => (
      <option key={option.value} value={option.value}>
        {option.label}
      </option>
    ))}
  </TextField>
</FormControl>

<FormControl dense>
  <TextField
    id="standard-select-currency"
    select
    label="Symbols"
    className={classes.textField}
    value={symbolSelect}
    onChange={handleSymbol}
    disabled={disabled}
    SelectProps={{
      native: true,
    }}
    helperText="Please select the exchanges"
  >
    {symbols.map((option) => (
      <option key={option.value} value={option.value}>
        {option.label}
      </option>
    ))}
  </TextField>
</FormControl>

home and hedge dropdowns require selections before enabling the symbol dropdown. The error is symbol dropdown won't enable until after home and hedge are selected more than once.

How can I change the methods I'm using to use useReducer instead (if this will fix the state updating issue)? I tried reading about it but don't understand what my switch cases would be.

Any help is appreciated, thank you.

Zero Cool
  • 2,541
  • 2
  • 8
  • 13

1 Answers1

1

"Set" operations are asynchronous. So when you do this:

setHomeSelect(event.target.value);
exchange_change();

While that second method call is invoked the variable homeSelect has not changed. It's going to change when the logic is done processing and the component prepares to re-render.

More to the point, this isn't very React-like:

const exchange_change = () => {
  if (homeSelect != 'Home' && hedgeSelect != 'Hedge'){
    //enable the symbol dropdown
    setDisabled(false);
  } else {
    //disable the select exchanges dropdown
    setDisabled(true);
  }
}

While you're modifying a state value here, conceptually you appear to be trying to modify the UI. Don't. The UI components can just respond to the state. So instead of that method you can do this in your "Symbols" component:

disabled={homeSelect == 'Home' || hedgeSelect == 'Hedge'}

If you want to put that into a method, put it into one that's invoked by the component rather than invoked before the re-render. For example:

disabled={() => checkSelects()}

And that method can be:

const checkSelects = () => 
  homeSelect == 'Home' || hedgeSelect == 'Hedge';

But overall just be aware of the order of operations here. All of the logic that gets invoked by the UI event is executed, which queues updates to state. Then state gets updated. Then components re-render (if necessary). Don't rely on the state being updated during that logic.

David
  • 208,112
  • 36
  • 198
  • 279
  • Makes sense but is so weird. Why would React read through ```setHomeSelect(event.target.value); exchange_change();``` and wait until the function is done to make the state change? Seems extra confusing for developers – Zero Cool Nov 03 '20 at 20:12