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.