Prerequisite
- Basic knowledge of React
- Basic knowledge of React Hook Form
What is a custom form item?
- A combination of one or more form items and their values
- Required
value
andonChange
as props
How to make a custom form item?
In this article we will make a select
of dog breeds and show a picture of that breed called DogSelect
Install react-hook-form
npm i react-hook-form
In your project, create a file called dog-select.tsx
, DogSelect
will have
- When mounted, it will fetch a list of dog breeds as
option
. - When select a breed, it will fetch a random dog picture of that breed.
- When select a breed, the form will validate if that breed is
corgi
or not -
value
is an object withbreed
is the name andimgUrl
Ignore the forwardRef
and ref
as we don't use them in this example.
import { forwardRef, useEffect, useState } from "react";
export type TDogSelectValue = {
breed: string;
imgUrl: string;
};
type TDogSelectProps = {
value: TDogSelectValue;
onChange: Function;
};
interface IDogApiResponse {
message: Object | Array<string> | string;
}
export const DogSelect = forwardRef<HTMLDivElement, TDogSelectProps>(({ value, onChange }: TDogSelectProps, ref) => {
const [breeds, setBreeds] = useState<string[]>([]);
useEffect(() => {
fetchAllBreeds().then((res) => setBreeds(res));
}, []);
const handleOnSelect = (e: React.ChangeEvent<HTMLSelectElement>) => {
const selectedBreed = e.target.value;
fetchRandomDogImgByBreed(selectedBreed).then((imgUrl) => {
const newValue: TDogSelectValue = {
breed: selectedBreed,
imgUrl,
};
onChange(newValue);
});
};
return (
<div className="dog-selector" ref={ref}>
<div style={{ display: "flex", flexDirection: "column", width: "300px" }} ref={ref}>
<option disabled value="pick a breed">
pick a breed
</option>
{breeds.map((b) => (
<option key={b} value={b}>
{b}
</option>
))}
</select>
{value.imgUrl && <img src={value.imgUrl} alt="dog picture" />}
</div>
);
});
const fetchAllBreeds = async () => {
const res = await fetch("https://dog.ceo/api/breeds/list/all");
const resJson: IDogApiResponse = await res.json();
return Object.keys(resJson.message);
};
const fetchRandomDogImgByBreed = async (breed: string) => {
const res = await fetch(`https://dog.ceo/api/breed/${breed}/images/random`);
const resJson: IDogApiResponse = await res.json();
return resJson.message as string;
};
Use DogSelect
in react-hook-form
in App.tsx
- When we select a new breed or press the submit button the form will be validated (
mode: "onChange"
) - If the selected breed is not
corgi
it will show an errorWrong dog breed
- If submit with no error an alert will pop out with breed name and image's URL.
import { useForm, Controller, SubmitHandler } from "react-hook-form";
import { DogSelect, TDogSelectValue } from "./components/dog-select";
type TFormValues = {
DogPicture: TDogSelectValue;
};
export default function App() {
const {
handleSubmit,
control,
formState: { errors },
} = useForm<TFormValues>({
defaultValues: {
DogPicture: {
breed: "pick a breed",
imgUrl: "",
},
},
mode: "onChange",
});
const onSubmit: SubmitHandler<TFormValues> = (data) => {
alert(`Submit succeeded!! breed: ${data.DogPicture.breed} url: ${data.DogPicture.imgUrl}`);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="DogPicture"
control={control}
rules={{
validate: (value: TDogSelectValue) => {
return value.breed === "corgi";
},
}}
render={({ field }) => <DogSelect {...field} />}
/>
{errors.DogPicture?.type === "validate" && (
<p role="alert" style={{ color: "red" }}>
Wrong dog breed
</p>
)}
<input type="submit" />
</form>
);
}
That's it! Thank you for reading this far. Till next time!
Source code: https://github.com/TheTeabagCoder/react-hook-form-custom-form-item-demo