Formik is an awesome library for handling forms in React.
🤔 Find forms in React hard to manage?
— Avi Aryan (@aviaryan123) January 31, 2019
Input states, Default values, Data validations, etc.
Try Formik and Yup. Makes handling forms super clean and easy.
As part of my contract work, I recently came into a problem which required me to use a country - state selector in a form handled by Formik. A quick Google search shows the react-country-region-selector package.
It's very straight-forward to use. Here is a sample code. To keep this article brief, I am only showing the country component.
<CountryDropdown
value={this.state.country}
onChange={(val) => this.setState({country: val})} />
My problem was how to use this with Formik. Luckily, it's very easy to do so. Have a look at the following code example.
import { Formik, Form } from 'formik';
import { CountryDropdown } from 'react-country-region-selector';
// ...
<Formik initialValues={{country: 'United States'}}>
{({ values, handleChange, handleBlur }) => (
<Form>
<CountryDropdown name="country" value={values.country}
onChange={handleChange} onBlur={handleBlur} />
</Form>
)}
</Formik>
But there's a catch. If you actually try to do something like this, it won't work. That is, the dropdown will display and show you the list of options but selecting an option won't actually change the dropdown value. I was stuck with this for a long time. Can you guess what the issue is?
After reading the documentation of both these packages between the lines, I got it. I missed the fact that onChange
prop in CountryDropdown
component returns the value of the component when it changes. The value
is not the same as the event callback that the normal DOM onChange
event returns. But Formik expects the normal onChange
event callback. That was the issue.
So how do we make CountryDropdown
return the actual event callback via onChange
. I looked into its documentation and there was no clue on how to do that. Then I went into its codebase and viewed the CountryDropdown.js file. It seems that it does return the original event via callback, it's just that it is not documented. It returns it as the second parameter of onChange
.
// CountryDropdown.js package
onChange: (e) => onChange(e.target.value, e)
So I changed my code to use the 2nd parameter of the callback for the e
value and fed it to handleChange
.
<CountryDropdown name="country" value={values.country}
onChange={(_, e) => handleChange(e)} onBlur={handleBlur} />
And obviousy, this worked. 🍰✨
And in case you are wondering how to use the RegionDropdown
component, it's very similar. We store its value in state
namespace and use values.country
for the country field.
import { CountryDropdown, RegionDropdown } from 'react-country-region-selector';
// ...
<RegionDropdown name="state" country={values.country} value={values.state}
onChange={(_, e) => handleChange(e)} onBlur={handleBlur} />