Skip to content

Instantly share code, notes, and snippets.

@hubgit
Last active December 29, 2023 03:41
Show Gist options
  • Star 131 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save hubgit/e394e9be07d95cd5e774989178139ae8 to your computer and use it in GitHub Desktop.
Save hubgit/e394e9be07d95cd5e774989178139ae8 to your computer and use it in GitHub Desktop.
Use react-select with Formik
const options = [
{ value: 'foo', label: 'Foo' },
{ value: 'bar', label: 'Bar' },
]
return <Field name={'example'} component={SelectField} options={options} />
import { FieldProps } from 'formik'
import React from 'react'
import Select, { Option, ReactSelectProps } from 'react-select'
export const SelectField: React.SFC<ReactSelectProps & FieldProps> = ({
options,
field,
form,
}) => (
<Select
options={options}
name={field.name}
value={options ? options.find(option => option.value === field.value) : ''}
onChange={(option: Option) => form.setFieldValue(field.name, option.value)}
onBlur={field.onBlur}
/>
)
@elie222
Copy link

elie222 commented Apr 18, 2020

@deanc you may want to avoid using the useFormik hook:
https://jaredpalmer.com/formik/docs/api/useFormik

Other than that looks good!

@joca2
Copy link

joca2 commented Apr 20, 2020

thank you: @deanc
thank you: @elie222

@joca2
Copy link

joca2 commented Apr 22, 2020

proposed change:

<Select
    ...
    value={
        options ?
            value ? options.find(option => option.value === value.toString()) : ''
            : ''
    }
    ...
/>

greetings to all

@Melanie-lab
Copy link

I copy pasted the raw example from the top, I only adapted typescript to javascript. The console displays a warning. Did anyone come across the same issues? Any ideas how to fix this?

index.js:1 Warning: Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.

  • Move data fetching code or side effects to componentDidUpdate.
  • If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state

Please update the following components: AutosizeInput, Select

@kablamus
Copy link

I'd suggest using the helpers from useField(). useField() is helpful any time you're working with elements that aren't inputs, like react-select.

Helpers can set the value, touched, and error state of a "Field".

function FormikSelect(...props) {
  const [field, meta, helpers] = useField(name="mySelectInput"); // can pass 'props' into useField also, if 'props' contains a name attribute
  const { setValue, setTouched, setError } = helpers;
  
  const setFieldProps = (selectedOption) => {
      setValue(selectedOption.value) 
      setTouched(true)
      setError(undefined)
  }
              
  return (
        <Select onChange={selectedOption => setFieldProps(selectedOption)} />
  );
};

@SuryaWebfox
Copy link

SuryaWebfox commented Jul 7, 2020

Here is my solution:

SelectField.js

import React from 'react';
import Select from 'react-select';
import {useField} from 'formik';

function SelectField(props) {
  const [field, state, {setValue, setTouched}] = useField(props.field.name);

  const onChange = ({value}) => {
    setValue(value);
  };

  return (
    <Select {...props} onChange={onChange} onBlur={setTouched}/>
  );
}

export default SelectField;

And in your Formik form simply use

<Field component={SelectField} name="fieldname" options={options}/>

where options has the format of

const options = [
   {value: 1, label: 'text'},
   ...
]

Edit: very similar to @kablamus solution

@ayush-shta
Copy link

thank you: @deanc and @elie222

@torian257x
Copy link

@SuryaVanLierde setting

<Formik initialValues={

doesnt seem to work with your solution

@justadhoc
Copy link

justadhoc commented Aug 18, 2020

Here is my approach:

import React from 'react';
import { Formik } from 'formik';
import Select from 'react-select';

const MySelect = props => {
    const options = [
        { value: 'chocolate', label: 'Chocolate' },
        { value: 'strawberry', label: 'Strawberry' },
        { value: 'vanilla', label: 'Vanilla' },
      ];


    return (
        <Formik
            initialValues={{
                flavors: ""
            }}
            onSubmit={values => alert(JSON.stringify(values, null, 2))}
            >
            {
                props => {
                    const {
                        values,
                        handleSubmit,
                        handleBlur,
                        setFieldValue
                    } = props;
                    return (
                        <form onSubmit={handleSubmit}>
                            <div style={{width: "200px", marginBottom: "15px"}}>
                                <Select
                                    id={"flavors"}
                                    type={"text"}
                                    value={values.flavors}
                                    onChange={option => setFieldValue("flavors", option)}
                                    options={options}
                                    onBlur={handleBlur}
                                />
                            </div>
                            <button>Submit</button>
                        </form>
                    );
                }
            }
        </Formik>
    );
}

export default MySelect;

@mihailsitnic
Copy link

Here is my approach:

import React from 'react';
import { Formik } from 'formik';
import Select from 'react-select';

const MySelect = props => {
    const options = [
        { value: 'chocolate', label: 'Chocolate' },
        { value: 'strawberry', label: 'Strawberry' },
        { value: 'vanilla', label: 'Vanilla' },
      ];


    return (
        <Formik
            initialValues={{
                flavors: ""
            }}
            onSubmit={values => alert(JSON.stringify(values, null, 2))}
            >
            {
                props => {
                    const {
                        values,
                        handleSubmit,
                        handleBlur,
                        setFieldValue
                    } = props;
                    return (
                        <form onSubmit={handleSubmit}>
                            <div style={{width: "200px", marginBottom: "15px"}}>
                                <Select
                                    id={"flavors"}
                                    type={"text"}
                                    value={values.flavors}
                                    onChange={option => setFieldValue("flavors", option)}
                                    options={options}
                                    onBlur={handleBlur}
                                />
                            </div>
                            <button>Submit</button>
                        </form>
                    );
                }
            }
        </Formik>
    );
}

export default MySelect;

👍

@quinceleaf
Copy link

quinceleaf commented Sep 19, 2020

I solved it by checking if there are any options available

const onChange = (option: ValueType<Option | Option[]>) => {
   setFieldValue(
      field.name,
      option ? (option as Option[]).map((item: Option) => item.value) : []
   );
});

This solution solved the multi-case for me (with react-select 3.1.0 and formik 2.1.5), but then would trigger TypeError: option.map is not a function for the single-case.

In trying to keep with the intent of @yassinedoghri's original code, aiming for a solution for both the single- and multi-case, the following change worked for me (I removed the Typescript annotations, but they should be easy to restore - I didn't do so as I am testing it in a non-TS application):

const onChange = (option) => {
    form.setFieldValue(
      field.name,
        !isMulti ? option.value
        : isMulti && option !== null ? option.map((item) => item.value)
        : []
    );
  };

@dqtkien
Copy link

dqtkien commented Oct 13, 2020

Here is my approach:

import React from 'react';
import { Formik } from 'formik';
import Select from 'react-select';

const MySelect = props => {
    const options = [
        { value: 'chocolate', label: 'Chocolate' },
        { value: 'strawberry', label: 'Strawberry' },
        { value: 'vanilla', label: 'Vanilla' },
      ];


    return (
        <Formik
            initialValues={{
                flavors: ""
            }}
            onSubmit={values => alert(JSON.stringify(values, null, 2))}
            >
            {
                props => {
                    const {
                        values,
                        handleSubmit,
                        handleBlur,
                        setFieldValue
                    } = props;
                    return (
                        <form onSubmit={handleSubmit}>
                            <div style={{width: "200px", marginBottom: "15px"}}>
                                <Select
                                    id={"flavors"}
                                    type={"text"}
                                    value={values.flavors}
                                    onChange={option => setFieldValue("flavors", option)}
                                    options={options}
                                    onBlur={handleBlur}
                                />
                            </div>
                            <button>Submit</button>
                        </form>
                    );
                }
            }
        </Formik>
    );
}

export default MySelect;

Hi, when I change from " value={values.flavors} ---> to value={name} " (name = `${values.flavors}` ), it doesn't work at all. The state is update correctly but it doesn't render value on react-select . Please let me know how to fix this. Because I want to make this Select as a component so I can re-use somewhere by passing the name of this input.

@Dollique
Copy link

I had some trouble getting this to work, as I wanted to also be able to call a custom function onChange.
As I don't use isMulti I didn't fix the issues with it!

This is my working solution so far:

import { FieldProps } from 'formik';
import React from 'react';
import Select, { OptionsType, ValueType } from 'react-select';

interface Option {
    label: string;
    value: string;
}

type IsMulti = boolean;

interface CustomSelectProps extends FieldProps {
    options: OptionsType<Option>;
    isMulti?: boolean;
    customOnChange?: Function;
}

export const SelectField = ({
    field,
    form,
    options,
    isMulti = false,
    customOnChange,
}: CustomSelectProps): JSX.Element => {
    const customOnChangeFn = (customFunction: Function, option: ValueType<Option | Option[], IsMulti>): void => {
        customFunction(option); // call a custom function and pass the selected option as a parameter
    };

    const onChange = (option: ValueType<Option | Option[], IsMulti>): void => {
        form.setFieldValue(
            field.name,
            isMulti ? (option as Option[]).map((item: Option) => item.value) : (option as Option).value
        );

        if (customOnChange !== undefined) {
            customOnChangeFn(customOnChange, option);
        }
    };

    const getValue = (): ValueType<Option, boolean> => {
        if (options) {
            return isMulti
                ? options.filter((option) => field.value.indexOf(option.value) >= 0)
                : options.find((option) => option.value === field.value);
        } else {
            return isMulti ? [] : ('' as any);
        }
    };

    return <Select name={field.name} value={getValue()} onChange={onChange} options={options} isMulti={isMulti} />;
};

export default SelectField;

@lucasrisatto
Copy link

Hello!
This is my multi select component:

import { FieldProps } from 'formik';
import React from 'react';
import Select, { OptionsType, ValueType } from 'react-select';

interface Option {
  label: string;
  value: string;
}

interface CustomSelectProps extends FieldProps {
  options: OptionsType<Option>;
  isMulti?: boolean;
}

const CustomSelect = ({
  field,
  form,
  options,
  isMulti = false,
}: CustomSelectProps) => {
  function onChange(option: ValueType<Option | Option[]>) {
    form.setFieldValue(
      field.name,
      option ? (option as Option[]).map((item: Option) => item.value) : [],
    );
  }

  const getValue = () => {
    if (options) {
      console.log('SelectMulti', field.value);
      return isMulti
        ? options.filter((option) => field.value.indexOf(option.value) >= 0)
        : options.find((option) => option.value === field.value);
    } else {
      return isMulti ? [] : ('' as any);
    }
  };

  return (
    <Select
      className="react-select-container"
      classNamePrefix="react-select"
      name={field.name}
      value={getValue()}
      onChange={onChange}
      options={options}
      isMulti={true}
      placeholder=" "
    />
  );
};

export default CustomSelect;

And i use it this way:

<Field
     name="permissions"
     id="permissions"
     component={CustomSelect}
     placeholder=" "
     options={permissions.map((permissions) => ({
         value: permissions.id,
         label: permissions.description,
     }))}
 />

This component works well for recording my records, however when I want to change, I receive the API data and the data is not loaded in my field.

Any help is appreciated.

@thomaslanenh
Copy link

Hey everybody. I've gotten this code to work with a single select via this:

`import Select from 'react-select';
import { useField } from 'formik';

export default function SelectField(props) {
const [field, state, { setValue, setTouched }] = useField(props.field.name);

const onChange = ({ value }) => {
setValue(value);
};

return <Select {...props} onChange={onChange} onBlur={setTouched} />;
}
`

and

<Field component={SelectField} name="campfeatures" options={selectObjects} />

How would I turn this into being capable of taking multi select?

Thanks for your help!

@cho0op
Copy link

cho0op commented Mar 11, 2021

That's how your onChange function must looks like to work with IsMulti
onChange={(value) => formik.setFieldValue( "SELECT_NAME", value.map((item) => item.value) ) }

@steelx
Copy link

steelx commented Mar 24, 2021

Thanks for the snippets! Here is a version for anyone who needs the multiple selection to work with formik (@tjugg):

import { FieldProps } from "formik";
import React from "react";
import Select from "react-select";
import { OptionsType, ValueType } from "react-select/lib/types";

interface Option {
  label: string;
  value: string;
}

interface CustomSelectProps extends FieldProps {
  options: OptionsType<Option>;
  isMulti?: boolean;
}

export const CustomSelect = ({
  field,
  form,
  options,
  isMulti = false,
}: CustomSelectProps) => {
  const onChange = (option: ValueType<Option | Option[]>) => {
    form.setFieldValue(
      field.name,
      isMulti
        ? (option as Option[]).map((item: Option) => item.value)
        : (option as Option).value
    );
  };

  const getValue = () => {
    if (options) {
      return isMulti
        ? options.filter(option => field.value.indexOf(option.value) >= 0)
        : options.find(option => option.value === field.value);
    } else {
      return isMulti ? [] : ("" as any);
    }
  };

  return (
    <Select
      name={field.name}
      value={getValue()}
      onChange={onChange}
      options={options}
      isMulti={isMulti}
    />
  );
};

thanks This works even with Formik and Chakra UI with bit changes :)

@ivan-sakoman
Copy link

I have issue here because onChange function require two types... this is the error: Generic type 'ValueType' requires 2 type argument(s).

i can set secound type to boolean but that makes me another problem, can anybody help me with this problem?

@db306
Copy link

db306 commented Jul 11, 2021

Updated above with couple typing and import fixes

import {FieldProps} from "formik";
import React from "react";
import Select, {OptionsType, ValueType} from "react-select";

interface Option {
    label: string;
    value: string;
}

interface FormikSelectProps extends FieldProps {
    options: OptionsType<Option>;
    isMulti?: boolean;
}

export const FormikSelect =
    ({
         field,
         form,
         options,
         isMulti = false,
     }: FormikSelectProps) => {
        const onChange = (option: ValueType<Option | Option[], boolean>) => {
            form.setFieldValue(
                field.name,
                isMulti
                    ? (option as Option[]).map((item: Option) => item.value)
                    : (option as Option).value
            );
        };

        const getValue = () => {
            if (options) {
                return isMulti
                    ? options.filter(option => field.value.indexOf(option.value) >= 0)
                    : options.find(option => option.value === field.value);
            } else {
                return isMulti ? [] : ("" as any);
            }
        };

        return (
            <Select
                name={field.name}
                value={getValue()}
                onChange={onChange}
                options={options}
                isMulti={isMulti}
            />
        );
    };

@FreTimmerman
Copy link

@db306

isMulti
    ? (option as Option[]).map((item: Option) => item.value)
    : (option as Option).value

instead of casting, you would better have a typeguard to let TS know the difference between option and option[]:

option instanceof Array
    ? option.map((item) => item.value)
    : option.value,

@hdsand
Copy link

hdsand commented Aug 17, 2021

This is not working for me: onBlur={field.onBlur}

The issue is that inside of Formik#handleBlur is the following code:

    var _a = e.target,
        name = _a.name,

It's assuming that the input that gets blurred has a name attribute which is the same as the name that formik is using to reference the field. But since the input attribute here is internal to react-select, it has a generic name. So after I blur, formik.touched is {"react-select-3-input":true}

It works if I do this instead:

       onBlur: function() {
            formik.setFieldTouched(field.name);
        }

Am I doing something wrong here? Is this onBlur approach working for other people?

thanks, it works!

@johnhailu
Copy link

johnhailu commented Sep 7, 2021

I hope it will help someone
Usage

const options = [
    { value: 'chocolate', label: 'Chocolate' },
    { value: 'strawberry', label: 'Strawberry' },
    { value: 'vanilla', label: 'Vanilla' }
  ]

<SelectInput name="courseAssignmentId" label="Course Assignment">
    {options.map((option) => (
    <option key={option.value} value={option.value}>
        {option.label}
    </option>
    ))}
</SelectInput>`

my implementation with bootstrap 5

/* eslint-disable react/prop-types */
import React from "react";

import { useField } from "formik";
import Select from "react-select";

function SelectInput({ label, ...props }) {
  const [field, meta, { setValue, setTouched }] = useField(props);
  const options = props.children.map((option) => ({
    value: option.props.value,
    label: option.props.children,
  }));

  const onChange = ({ value }) => {
    setValue(value);
  };

  return (
    <div className="mb-3">
      <label htmlFor={props.id || props.name} className="form-label">
        {label}
      </label>
      <Select
        defaultValue={options.find((option) => option.value === field.value)}
        options={options}
        onChange={onChange}
        onBlur={setTouched}
      />
      {meta.touched && meta.error ? (
        <div className="form-text text-danger">{meta.error}</div>
      ) : null}
    </div>
  );
}
export default SelectInput;

@Matt-Hill-83
Copy link

This is great. thx!

@jimryanzulueta
Copy link

jimryanzulueta commented Sep 27, 2021

Did react-select just change its type definitions? Definitely not seeing OptionsType and ValueType

@asgeo1
Copy link

asgeo1 commented Oct 13, 2021

Did react-select just change its type definitions? Definitely not seeing OptionsType and ValueType

Yes, in v5 the types are different: https://react-select.com/upgrade

OptionsType => Options
ValueType => OnChangeValue

@ocobble
Copy link

ocobble commented Jan 7, 2022

Here's my implementation of an async multi select component used with Formik. This gist was really helpful for me in creating this, but I didn't see any other posts with both async and multi select so I'll add it here.

import { useField, FieldProps } from "formik";
import React from "react";
import AsyncSelect from "react-select/async"

const getOptions = () => {
     return [{value: "1", label: "Test one"}, {value: "2", label: "Test two"}];
  };

  const promiseOptions = () =>
    new Promise((resolve) => {
        timeout(resolve(getOptions()),
        1000);
    });

const asyncMultiSelect = ({
  label,
  ...props
}) => {

  const [field, meta, helpers] = useField(props);

  const { setValue } = helpers;

  const onChange = (option) => {
    setValue(
       (option).map((item) => item.value)
     );
  };

  return (
    <div>
      <label>{label}</label>

      <AsyncSelect
        defaultOptions
        loadOptions={promiseOptions}
        name={field.name}
        onChange={onChange}
        isMulti
      />
      
    </div>
  );
};

export default asyncMultiSelect;

Here's the code in the file with the Formik form:

<Field as={asyncMultiSelect} label="Make a selection" name="mySelect" />

@ken-muturi
Copy link

ken-muturi commented Apr 16, 2023

Updated above with couple typing and import fixes

import {FieldProps} from "formik";
import React from "react";
import Select, {OptionsType, ValueType} from "react-select";

interface Option {
    label: string;
    value: string;
}

interface FormikSelectProps extends FieldProps {
    options: OptionsType<Option>;
    isMulti?: boolean;
}

export const FormikSelect =
    ({
         field,
         form,
         options,
         isMulti = false,
     }: FormikSelectProps) => {
        const onChange = (option: ValueType<Option | Option[], boolean>) => {
            form.setFieldValue(
                field.name,
                isMulti
                    ? (option as Option[]).map((item: Option) => item.value)
                    : (option as Option).value
            );
        };

        const getValue = () => {
            if (options) {
                return isMulti
                    ? options.filter(option => field.value.indexOf(option.value) >= 0)
                    : options.find(option => option.value === field.value);
            } else {
                return isMulti ? [] : ("" as any);
            }
        };

        return (
            <Select
                name={field.name}
                value={getValue()}
                onChange={onChange}
                options={options}
                isMulti={isMulti}
            />
        );
    };

I am having issues with my form using this custom component. The values are not added to formik field values. To explain in detail, I have a select which change, determines the values to pre-select on my custom react-select input (note values here not options). This also cascades this down to a 3rd input. These is a cascading inputs seem to be getting the values, however, the this values are on in the field value object. Please note that because this is a dynamic input am not able to use initialValues object. So i somewhat use the formik props to get the selected values and cascade down. see screenshots below

image

image

image

@alexluongfm
Copy link

React select (single select) that uses more of the formik hooks to reduce props passing and keep everything as simple as possible. Must be under the <Formik>/<Form> code to ensure hooks all work properly.

import React from 'react';
import Select, { Options } from 'react-select';
import { useField } from 'formik';

type Props = {
  selectOptions: Options[],
  formikFieldName: string,
  placeholder?: string,
};

/**
 * React Select but hooked into Formik
 * @returns {JSX.Element}
 * @constructor
 */
const FormikSelect = ({
  selectOptions,
  formikFieldName,
  placeholder,
}: Props) => {
  // eslint-disable-next-line no-unused-vars
  const [field, _, helpers] = useField(formikFieldName);
  const { setValue } = helpers;

  return (
    <Select
      defaultValue={selectOptions.find(
        (option) => option.value === field.value
      )}
      options={selectOptions}
      placeholder={placeholder}
      onBlur={field.onBlur}
      onChange={(option) => setValue(option.value)}
    />
  );
};

FormikSelect.defaultProps = {
  placeholder: '',
};

export default FormikSelect;

Example usage

        <FormikSelect
          selectOptions={[
            { value: 'foo', label: 'Foo' },
            { value: 'bar', label: 'Bar' },
          ]}
          formikFieldName="formikInitVals.shippingMethods"
          placeholder="Shipping Methods"
        />

Versions

formik: 2.4
react: 18.2

@rever96
Copy link

rever96 commented Aug 11, 2023

@alexluongfm with your versions still gives Generic type 'Options' requires 1 type argument(s). error on line 6

"react-select": "^5.7.4",

@IlyaZha
Copy link

IlyaZha commented Sep 22, 2023

My Solution for react-select@^5.7.4:

export default function MultipleSelect({ fieldName, placeholder, ...props }) {
  const [field, meta, { setValue }] = useField(fieldName);
  const options = props.children.map((option) => ({
    value: option.props.value,
    label: option.props.children,
  }));

  const onChange = (selectedOptions: MultiValue<any>) => {
    setValue(selectedOptions);
  }

  return (<>
    <Select
      isMulti={true}
      defaultValue={options.find((option) => option.value === field.value)}
      placeholder={placeholder}
      onChange={onChange}
      options={options}
      onBlur={field.onBlur}
    />
  </>);
}

Form of select should be wrapped with <Formik></Formik>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment