Formik is an awesome library for handling forms in React.

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} />