React
Posted By Sebastian

Modern React From The Beginning EP11: Reducer Hook For Advanced State Management


Subscribe On YouTube

Episodes

We’ve been creating a state in our components by using the useState hook. However React provides you with another way of managing state in your functional component: the useReducer hook. In general useReducer is preferable to useState when you have a complex state logic. This maybe the case when the state logic involved multiple sub-values or when the next state value depends on the previous state value.

In this episode we’ll change our existing implementation and make use of the useReducer hook to create and manage the courses state within App component.

Importing The Reducer Hook

First let’s extend the first import statement in App.js to also include useReducer:

import React, {useState, useEffect, useReducer} from 'react';

Implementing The Reducer Function

Next, we need to implement a reducer function. The reducer function is implemented in App.js outside of the App component implementation and contains the logic which is needed to calculate and return new values for the state:

const coursesReducer = (state, action) => {
  switch(action.type) {
    case 'SET_COURSES':
      return action.payload;
    default:
      throw new Error();
  }
};

As you can see the method takes two parameters:

  • state: gives you access to the current state object
  • action: gives you access to the object describing the action

A reducer function can handle multiple types of actions. The type of action is determined by using the action.type string. For the example above we’re just handling of action type which is ‘SET_COURSES’. With this action we’re settings the state with the data which is handed over in action.payload. As there is no complex logic involved in calculating the new state value we just need to return what’s available in action.payload. The value which is returned here is the value which is then assigned to the new state.

Later on we’ll use the coursesReducer function to handle multiple action types which also will involve calculating the new state value based on the old value by applying a more complex logic then in this first example.

For now we’ll stick with this first example by handling the action type SET_COURSES and handling the default case (which is invoked when no condition is met).

Change From useState to useReducer

The reducer function is prepared outside of the App function. Within the App function we now need to change from useState to useReducer when creating the courses state:

  const [courses, dispatchCourses] = useReducer(
    coursesReducer,
    []
  );

The useReducer function takes two paramater:

  • the reducer function which is used to create and return a new state
  • The initial state value which is just an empty array in our case (because we’re setting the courses data after a delay)

In return we’re getting an array consisting of:

  • the state variable
  • the dispatcher function which is used to dispatch actions to change the change which are then handled by the reducer function

Dispatch The Action

Now we need to change the useEffect implementation which is used to simulate asynchronous data loading slightly:

  useEffect(() => {
    setIsLoading(true);
    getCoursesAsync().then(result => {
      dispatchCourses({
        type: 'SET_COURSES', 
        payload: result.courses
      });
      setIsLoading(false);
    })
  }, []);

Once the promise is resolved we’re calling the the disptachCourses dispatcher function. To update the state with the array of courses we’re passing in an object which consists of two property:

  • type: The type of action we’d like to get handled by the reducer function.
  • payload: Data which is needed to calculate and set the new state value.

In our case we’d like to dispatch the SET_COURSES action, so we need to set the type property to the string ‘SET_COURSES’. The payload property is set to the array of courses which is available via result.courses.

Trying out the application in the browser again, you’ll now see that still everything is working. However to underlying state management of now exchanged to a useReducer-based approach.

Again, in the following you can take a look at the complete source code in App.js after having made all changes from this episode:

import React, {useState, useEffect, useReducer} from 'react';
import CoursesList from './CoursesList';
import Search from './Search';

const courses_data = [
  ...
];

const coursesReducer = (state, action) => {
  switch(action.type) {
    case 'SET_COURSES':
      return action.payload;
    default:
      throw new Error();
  }
};

const App = () => {
  const [courses, dispatchCourses] = useReducer(
    coursesReducer,
    []
  );

  const [isLoading, setIsLoading] = useState(false);

  const [searchText, setSearchText] = useState(
    localStorage.getItem('searchText') || ''
  );

  const handleSearch = event => {
    setSearchText(event.target.value);
  }

  const getCoursesAsync = () => 
    new Promise(resolve => 
        setTimeout(
          () => resolve({courses: courses_data}),
          2000
        )
      );
  
  useEffect(() => {
    setIsLoading(true);
    getCoursesAsync().then(result => {
      dispatchCourses({
        type: 'SET_COURSES', 
        payload: result.courses
      });
      setIsLoading(false);
    })
  }, []);

  useEffect(() => {
    localStorage.setItem('searchText', searchText)
  }, [searchText]);

  const filteredCourses = courses.filter(course => {
    return course.title.includes(searchText) || course.author.includes(searchText);
  });

  return (
    <div>
      <h1>List of Courses</h1>
      <hr />

      <Search value={searchText} onSearch={handleSearch} />
      
      {isLoading ? (
        <p>Loading Courses ...</p>
      ) : (
        <CoursesList courses={filteredCourses} />
      )}
      
    </div>
  );
}

export default App;

Advertisement: Top 3 React Online Courses

If you want to dive deeper and become a React pro also consider taking one of the following great online courses.

React – The Complete Guide (incl Hooks, React Router, Redux)*
Dive in and learn React.js from scratch! Learn Reactjs, Hooks, Redux, React Routing, Animations, Next.js and way more!

Go To Course*

Modern React with Redux*
Master React v16.6.3 and Redux with React Router, Webpack, and Create-React-App. Includes Hooks!

Go To Course*

The Complete React Developer Course (w/ Hooks and Redux)*
Learn how to build and launch React web applications using React, Redux, Webpack, React-Router, and more!

Go To Course*

* Affiliate Link / Advertisement: This blog post contains affiliate links, which means that if you click on one of the product links and buy a product, we’ll receive a small commission. There are no additional costs for you. This helps support the channel and allows us to continue to make videos like this. Thank you for the support!


Using and writing about best practices and latest technologies in web design & development is my passion.