React image

React Hooks: useReducer

akpojotor shemi
4 min readAug 3, 2020

In a recent blog we showed how to build the demo app pictured below using functional components with React Hooks- useState. The demo app takes a student name and grade as inputs, and appends the inputs to a list.

picture of demo app

The components tree of the demo app is showed below. The app has two child components; FunctionForm and StudentContainer . Student is a child component of StudentContainer

component tree of the demo app

multiple useState

  • We used useState to pass in the original list/data to the App component
  • We used useState to managed state in the FunctionForm component via controlled form.
  • We used useState to handle form submission and to add input data from the form to existing list.

In this blog, we useReducer to managed state instead of using multiple useState mentioned in the list above.

[useReducer is ] An alternative to useState. [useReducer] Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method. (If you’re familiar with Redux, you already know how this works.)

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.

In the following sections, we show how to replace each useState with a reducer type paired with a dispatch method. But first, here is what the App component and reducer function looks like;

app component

In this component, the useReducer takes an initialState const initialState={students: [], name: "", grade: ""}and a reducer type, paired with a dispatch method and returns the variable’s current state — students, name, grade

The application state is in the App component. We pass down callback function and pertinent state variables to FunctionForm component to remotely managed state from the App component. The students variable is passed to the studentsContainer, where each student is passed down to the student component.

reducer function

The reducer function handles three action cases; “addData”, “inputField” and “addOneStudent” for loading data, controlled form and form submission respectively.

load/render data

We used useEffect to trigger a dispatch action that loads the data into the App component. This dispatch action is triggered when the page first mounts or after any subsequent page rendering. The reducer type “addData” paired with dispatch action is also shown below. We did not have to useEffect in this scenario, we could have just set the initialState.students to local data. If we were to fetch data from an external API, it would be done with useEffect.

useEffect(() => {
dispatch({ type: "addData", data });
}, []);
// reducer to handle "addData" action type
case "addData":
return { ...state, students: action.data };

with useState we would have done something like;

const [students, setStudents] = useState(data);
OR
const [students, setStudents] = useState([]);
useEffect(() => {
setStudents(data);
}, []);

controlled form

We useReducer to handled controlled form and the corresponding operation within the reducer function is shown below

const handleChange = (name, value) => {
dispatch({ type: "inputField", fieldname: name, value: value });
};
// reducer to handle "inputField" action type
case "inputField":
return { ...state, [action.fieldname]: action.value };

with useState, we would have called the useState function twice and passed-in two separate callback functions to the form’s input onChange attribute.

const [name, setName] = useState("");
const [grade, setGrade] = useState("")
const handleNameChange = (e) => {
setName(e.target.value);
};
const handleGradeChange = (e) => {
setGrade(e.target.value);
}

form submission

The following dispatch action shown below is triggered when the form is submitted. The corresponding operation within the reducer function is shown below. A new student object is appended to the exciting students list without mutating state.

const addStudents = (info) => {
dispatch({ type: "addOneStudent", info });
};
// reducer to handle "addOneStudent" action type
case "addOneStudent":
let newId = state.students.length + 1;
let newStudent = {
grade: action.info.grade,
name: action.info.name,
id: newId,
};
return { ...state, students: [...state.students, newStudent] };

The useState version, shown below, would look similar to how we handled form submission with useReducer

const [students, setStudents] = useState(data);
const addStudentFunction = (info) => {
let newId = students.length + 1;
let newStudent = { ...info, id: newId };
let newStudents = [...students, newStudent];
setStudents(newStudents);
};

Summary.

  • we discussed when and how to replace useState with useReducer.
  • we covered useReducer in react controlled forms.

The github repository for the demo app is here

--

--