Search code examples
reactjsreact-bootstrapformik

Re-populate a Select dropdown depending on another selection in a Formik form


I have two dropdowns where selecting an option on the right should update the options on the left. enter image description here

The 1st one is frequencyDays, the 2nd is frequencyInterval.

Suppose I have a function that will return an array of <option>s for a given ID (the value of the 2nd param)

const getOptionsDays = (value) => {
    let options = [];
    //... some logic in a loop ...
    for (var i = 0; i < N; i++) {
       options.push(<option key={i} value={i}>{i}</option>);
    }
    return options; // Returns an array of <option> elements
}

The Formik form pre-populates correctly on initialization, but does not update.

First Dropdown (frequencyDays)

<Form.Control as="select"
  id="dropdownFrequencyDays"
  name="frequencyDays"
  value={values.frequencyDays}
  onChange={handleChange}
>
    <option></option>
    { getOptionsForDays(values.frequencyInterval) }
</Form>

Second Dropdown (frequencyInterval), onChange should trigger repopulation

<Form.Control as="select"  
              id="dropdownFrequencyInterval"
              name="frequencyInterval"
              value={values.frequencyInterval}
              onChange={e => /* Should do something here but getting syntax errors */
                         // Call built-in Formik handleChange 
                         handleChange(e);
                         // Additional: call to repopulate 1st dropdown?
                         // ...errors
                       }
>

I want to let Formik do its form binding but in addition call the repopulation of the 1st dropdown, but getting errors.


Solution

  • I was close. The solution is to keep a state var. with your <option> array. Then onChange, remember the syntax is onChange={e => { .. }} (double brace), includes both the default Formik handleChange + custom state setter.

    // State var to keep option array
    const [frequencyDayValues, setFrequencyDayValues] = useState([]);
    
    ...
    
    // On initialization, set your option array (with whatever logic needed, 
    // e.g. populateOptions() for an initial code of 55)
    useEffect(() => {    
        setFrequencyDayValues(populateOptions(55));
    }, []);
    
    // The option array must contain actual <option> elements, e.g.
    const populateOptions = (value) => {
        let options = [];
        options.push(<option value={..}>Label</option>);
        return options;
    }
    ...
    
    {/* Dependent Dropdown: Displays whatever is currently in frequencyDayValues */}
    <Form.Control as="select"
                  name="frequencyDays"
                  value={values.frequencyDays}
                  onChange={handleChange}
    >
        <option></option>
        {/* Populate Frequency Days from current state variable */}
        {frequencyDayValues}
    </Form.Control>
    
    {/* Triggering Dropdown: onChange does both the Formik handleChange AND custom update */}
    <Form.Control as="select" 
                  name="frequencyInterval"
                  value={values.frequencyInterval}
                  onChange={e => {
                      // Call default Formik handleChange()
                      handleChange(e);
                      // Additionally set the new frequencyDayValues state variable
                      // based on e.target.value
                      setFrequencyDayValues(populateOptions(e.target.value));
                    }
                  }                                                      
    >