-
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.
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} | |
/> | |
) |
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;
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;
👍
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)
: []
);
};
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.
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;
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.
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!
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 :)
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?
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}
/>
);
};
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,
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!
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;
This is great. thx!
Did react-select just change its type definitions? Definitely not seeing OptionsType and ValueType
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
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" />
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
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
@alexluongfm with your versions still gives Generic type 'Options' requires 1 type argument(s).
error on line 6
"react-select": "^5.7.4",
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>
I just did this...
onValueChange={(val) => formik.setFieldValue('timezone', val)}
Here is my solution:
SelectField.js
And in your Formik form simply use
where options has the format of
Edit: very similar to @kablamus solution