Skip to content

Instantly share code, notes, and snippets.

@JustAyush
Created September 14, 2021 11:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JustAyush/b106ea77ea13d75a10453d94f93383ce to your computer and use it in GitHub Desktop.
Save JustAyush/b106ea77ea13d75a10453d94f93383ce to your computer and use it in GitHub Desktop.
React Select usage with react-hook-form
import {
Button,
FormControl,
FormErrorMessage,
FormLabel,
Link,
SimpleGrid,
Stack,
} from '@chakra-ui/react';
import { yupResolver } from '@hookform/resolvers/yup';
import React, { ReactElement, useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import ReactSelect from 'react-select';
import * as yup from 'yup';
import SubmitDataDisplay from '../../components/submit-data-display';
import repos from '../../constants/repos';
const avengerOptions = [
{ value: 'Iron Man', label: 'Iron Man' },
{ value: 'Captain America', label: 'Captain America' },
{ value: 'Spiderman', label: 'Spiderman' },
{ value: 'Thor', label: 'Thor' },
{ value: 'The Hulk', label: 'The Hulk' },
{ value: 'Captain Marvel', label: 'Captain Marvel' },
];
type ReactSelectOption = {
value: string | number;
label: string;
unit?: string;
};
type FormData = {
favouriteAvenger: ReactSelectOption | null;
favouriteAvengerPersisted: ReactSelectOption | null;
};
const validationSchema = yup.object({
// Instead of having required on individual properties of shape schema, if you are sure all the properties
// are mandatory, the required function can be called on the object shape directly.
favouriteAvenger: yup
.object()
.shape({
label: yup.string(),
value: yup.string(),
})
.nullable()
.required('Favourite Avenger is required'),
favouriteAvengerPersisted: yup
.object()
.shape({
label: yup.string(),
value: yup.string(),
})
.nullable()
.required('Favourite Avenger is required'),
});
const defaultValues = {
favouriteAvenger: null,
favouriteAvengerPersisted: null,
};
export default function (): ReactElement {
const {
handleSubmit,
formState: { errors },
control,
reset,
setValue,
} = useForm<FormData>({
resolver: yupResolver(validationSchema),
defaultValues: defaultValues,
});
// Draft to the formData stored in localstorage
const [draft, setDraft] = useState<{
favouriteAvengerPersisted: ReactSelectOption | null;
} | null>(null);
// It is recommend to extract required properties (value in this case)
// from react-select in onSubmitHandler
const onSubmit = (data: FormData) => {
const dataToSubmit = {
favouriteAvenger: data.favouriteAvenger?.value,
favouriteAvengerPersisted: data.favouriteAvengerPersisted?.value,
};
alert(JSON.stringify(dataToSubmit));
};
const onReset = () => {
reset(defaultValues);
setDraft(null);
localStorage.removeItem('favouriteAvengerPersisted');
};
const handleFieldChange = (
fieldName: 'favouriteAvengerPersisted',
value: ReactSelectOption | null
) => {
setDraft({
[fieldName]: value,
});
localStorage.setItem(fieldName, JSON.stringify(value));
};
useEffect(() => {
const value = localStorage.getItem('favouriteAvengerPersisted');
if (!value) return;
const parsedValue = JSON.parse(value);
setDraft({ favouriteAvengerPersisted: parsedValue });
setValue('favouriteAvengerPersisted', parsedValue);
}, [setValue]);
return (
<Stack spacing="10">
<SimpleGrid gap="10" columns={2}>
<form onSubmit={handleSubmit(onSubmit)}>
<Stack spacing="6">
<FormControl isInvalid={!!errors?.favouriteAvenger}>
<FormLabel>Favourite Avenger</FormLabel>
<Controller
control={control}
name="favouriteAvenger"
render={({ field }) => (
<ReactSelect
id="favouriteAvenger"
{...field}
placeholder="Choose option"
options={avengerOptions}
/>
)}
/>
<FormErrorMessage>
{errors?.favouriteAvenger && errors?.favouriteAvenger?.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={!!errors?.favouriteAvenger}>
<FormLabel>
Favourite Avenger (Persisted with Local Storage)
</FormLabel>
<Controller
control={control}
name="favouriteAvengerPersisted"
render={({ field: { onChange, onBlur, ref } }) => (
<ReactSelect
id="favouriteAvengerPersisted"
onBlur={onBlur}
onChange={(selectedOption) => {
onChange(selectedOption);
handleFieldChange(
'favouriteAvengerPersisted',
selectedOption
);
}}
value={draft?.favouriteAvengerPersisted || null}
inputRef={ref}
placeholder="Select option"
options={avengerOptions}
/>
)}
/>
<FormErrorMessage>
{errors?.favouriteAvengerPersisted &&
errors?.favouriteAvengerPersisted?.message}
</FormErrorMessage>
</FormControl>
<Button colorScheme="primary" type="submit">
Submit
</Button>
<Button colorScheme="gray" onClick={onReset}>
Reset
</Button>
</Stack>
</form>
<SubmitDataDisplay control={control} />
</SimpleGrid>
<Link
href={repos.chakraUIFormFields.input}
isExternal
color="primary.main"
_focus={{ outline: 'none' }}>
View Source
</Link>
</Stack>
);
}
@nosirovbehzodjon
Copy link

hello. ReactSelect does not have inputRef prop. When I used as Custom component , I wrapp forwardRef and now I have ref type error. Can you help me show ref's type?

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