'Formik - Upon component mount select first option in dropdown list

On this StackBlitz:

https://stackblitz.com/edit/react-formik-yup-example-uhdg-4dzpq5?file=Registration.js

I have the following code:

import { ErrorMessage, Field, Form, Formik } from 'formik';
import { get } from 'lodash-es';
import React, { useEffect } from 'react';
import * as Yup from 'yup';

const DropdownListInput = ({ value, children, handleChange }) => {
  useEffect(() => {
    const firstOptionValue = get(children, '[0].props.value', '');
    if (value === '' && firstOptionValue !== '') {
      // using setTimeout is a workaround and it should be removed.
      setTimeout(() => {
        handleChange(firstOptionValue);
      }, 0);
    }
  }, []);

  return (
    <select value={value} onChange={(e) => handleChange(e.target.value)}>
      {children.map(({ props: { value, children: text } }, index) => (
        <option value={value} key={index}>
          {text}
        </option>
      ))}
    </select>
  );
};

export default () => {
  return (
    <Formik
      initialValues={{
        email: '',
      }}
      validationSchema={Yup.object().shape({
        email: Yup.string()
          .required('Email is required.')
          .email('Email is invalid.'),
      })}
      onSubmit={(values, { setSubmitting }) => {
        console.log(values);
        setSubmitting(false);
      }}
      enableReinitialize
      validateOnMount
    >
      {(formik) => {
        return (
          <Form>
            <div>
              <Field
                component={DropdownListInput}
                formik={formik}
                name="email"
                value={formik.values.email}
                handleChange={(value) => {
                  console.log(value);
                  formik.setFieldValue('email', value);
                }}
              >
                <option value="[email protected]">Bill Bates</option>
                <option value="[email protected]">Steve Jobs</option>
                <option value="[email protected]">Elon Musk</option>
              </Field>
              <ErrorMessage name="email">
                {(error) => <div style={{ color: '#f00' }}>{error}</div>}
              </ErrorMessage>
            </div>
            <input type="submit" value="Submit" disabled={!formik.isValid} />
          </Form>
        );
      }}
    </Formik>
  );
};

My problem is: For it to work, I needed to use a setTimeout(...).

I just want to get rid of the setTimeout(...) and just leave the instruction inside of it.

Requirement:

  • I need the Submit button to get enabled automatically when the component loads.
  • Setting the value for the email has to come from the DropdownListInput itself similarly to what I currently have on the code above.

Any idea on how to achieve this?

If you want you can post your forked StackBlitz.

Thanks!



Solution 1:[1]

I suggest listening to the change of firstOptionValue, and executing handleChange once it was set.

Suggested modification on DropdownListInput component:

  const [firstOptionValue, setFirstOptionValue] = useState('')

  useEffect(() => {    
    setFirstOptionValue(get(children, '[0].props.value', ''));    
  }, []);

  useEffect(() => {
    if (value === '' && firstOptionValue !== '') {      
      handleChange(firstOptionValue);      
    }
  }, [firstOptionValue])

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andrew Li