Use reducer hook for management states

May 7, 2023

In react you can use useState hook of save data, but is very complexity if you have many state in one component, an alternative is reducer, its hook is reducer your states use object , dispath and events .

lets see a example bad code with many states:

1import { FunctionComponent, useState } from "react";
2
3const Form: FunctionComponent = () => {
4 const [email, setEmail] = useState("");
5 const [name, setName] = useState("");
6 const [password, setPassword] = useState("");
7
8 return (
9 <div>
10 <input
11 type="text"
12 value={name}
13 placeholder="Name"
14 required
15 onChange={({ target }) => {
16 setName(target.value);
17 }}
18 />
19
20 <input
21 type="email"
22 required
23 value={email}
24 placeholder="Email"
25 onChange={({ target }) => {
26 setEmail(target.value);
27 }}
28 />
29 <input
30 required
31 type="password"
32 placeholder="password"
33 value={password}
34 onChange={({ target }) => {
35 setPassword(target.value);
36 }}
37 />
38 </div>
39 );
40};
41
42export default Form;

This is examples is very simple but in think that is a big form in many inputs, for each input your create one state… yes, this is not good.

useReducer

hook useReducer work similar useState , but in one funcion you can put many values without collateral effect and for update on value you use event handlers.. basically you send an event in you reducer and it`s find a method with same event and its executed

Let’s refactor Form component for use reducer, first we have created other function outside in Form

something like:

1function reducer() {
2 throw Error("Unknown action.");
3}

I’ll use Typescript for showing, then we need create type for parms of reducer

1enum actionNames {
2 EMAIL = "EMAIL",
3 NAME = "NAME",
4 PASSWORD = "PASSWORD",
5}
6
7type actionType = {
8 type: actionNames;
9 nextValue: string;
10};
11
12type formType = {
13 name: string;
14 email: string;
15 password: string;
16};
17
18function reducer(state: formType, action: actionType) {
19 throw Error("Unknown action.");
20}

I created three enums EMAIL , NAME and PASSWORD and a assign value that we will a use as event, and actionType I created two types the first is type of event and a assign with enum actionNames and the below line have nextValue is a string but all inputs is some type, We’ll passed types in function own reducer .

1const { type, nextValue } = action;
2switch (type) {
3 case actionNames.EMAIL: {
4 return {
5 ...state,
6 email: nextValue,
7 };
8 }
9 case actionNames.PASSWORD: {
10 return {
11 ...state,
12 password: nextValue,
13 };
14 }
15
16 case actionNames.NAME: {
17 return {
18 ...state,
19 name: nextValue,
20 };
21 }
22}

basically, We will verify if type is equal of one enum, exemple if type is equal EmaiL will return old state and change email with new value that is action param make that with each action

1enum actionNames {
2 EMAIL = "EMAIL",
3 NAME = "NAME",
4 PASSWORD = "PASSWORD",
5}
6type actionType = {
7 type: actionNames;
8 nextValue: string;
9};
10
11type formType = {
12 name: string;
13 email: string;
14 password: string;
15};
16
17function reducer(state: formType, action: actionType) {
18 const { type, nextValue } = action;
19 switch (type) {
20 case actionNames.EMAIL: {
21 return {
22 ...state,
23 email: nextValue,
24 };
25 }
26 case actionNames.PASSWORD: {
27 return {
28 ...state,
29 password: nextValue,
30 };
31 }
32
33 case actionNames.NAME: {
34 return {
35 ...state,
36 name: nextValue,
37 };
38 }
39 }
40
41 Error("Unknown action: " + action.type);
42}

Now we going use this function with reducer hook, lets go remove all useSate and call useReducer hook and frist param its receive an reducer function that we just created, second param receive a initial values

1const [state, dispatch] = useReducer(reducer, {
2 email: "",
3 password: "",
4 name: "",
5});

lets created handleclick function for update values in reducer

1function handleClick(nextValue: string, type: actionNames) {
2 dispatch({ type, nextValue });
3}

this function use a dispatch to useReducer in dispatch receive a object with type

event and new value, let updates inputs for sported reducer

1<div>
2 <input
3 type="text"
4 value={state.name}
5 placeholder="Name"
6 required
7 onChange={({ target }) => {
8 handleClick(target.value, actionNames.NAME);
9 }}
10 />
11
12 <input
13 type="email"
14 required
15 value={state.email}
16 placeholder="Email"
17 onChange={({ target }) => {
18 handleClick(target.value, actionNames.EMAIL);
19 }}
20 />
21 <input
22 required
23 type="password"
24 placeholder="password"
25 value={state.password}
26 onChange={({ target }) => {
27 handleClick(target.value, actionNames.PASSWORD);
28 }}
29 />
30</div>

for each input value use a object for access current value.