How to Manage State with Zustand in React

Published by codingmaster on

How to Manage State with Zustand in React

If you want to manage a complex state in React, which tool would you use? You might say React Context, Redux, or Jotai. But all of these tools are quite complex to use, except React Context API. Though it cannot be used for more complex tasks it’s good for simple state management.

Redux on the other hand is the most popular and a sort of industry-standard tool for managing complex states in React. Again, it’s quite complex to start with and is a quite heavy bundle.

So, how about a library which is lighter than Redux and manages complex states?

Today, we’ll learn about a newer React State Management library called Zustand. We’ll create a simple Course Management Application, where users will be able to add, remove and change course completion status.

What is Zustand?


Zustand is a fast, scalable, and small state-management library for React. It is primarily dependent on hooks, so it doesn’t have much of a boilerplate code.

Let’s Get Started ⇒

  • First, create a new React App or head over to the CodeSandBox and create a new React App in just seconds.
  • Install Zustand with yarn add zustand or npm install zustand
  • Create a folder src ⇒ app ⇒ courseStore.js
  • Here we’ll write all of the Zustand logic or manage state.
  • Create another folder src ⇒ components ⇒ CourseForm.jsx & CourseList.jsx


#1 Creating Course Store ⇒


Inside courseStore.js, paste the code below.

import create from "Zustand";
import { devtools, persist } from "zustand/middleware";

First, we are importing some Zustand methods like create, which will help us to create a store. devtools will talk about it later and persist allows us to store data in browser local storage.

Now, let’s create the courseStore and addCourse action.

const courseStore = (set) => ({
    courses: [],
    addCourse: (course) => {
        set((state) => ({
            courses: [course, ...state.courses],
        }))
    }
});

Here, we have created an arrow function that takes an argument set, which allows us to set new states. Just like hooks.

This function returns, courses, which is a list of courses. The addCourse is an action that will take the new course and add it to courses using set method.

The set(), gives the previous state or the latest snapshot of the state. Now, what we are doing is adding the new course to the course list, and spreading out the previous courses.

Similarly removing a course is as simple as that,

const courseStore = (set) => ({
    courses: [],
    addCourse: (course) => {
        set((state) => ({
            courses: [course, ...state.courses],
        }))
    },
    removeCourse: (courseId) => {
        set((state) => ({
            courses: state.courses.filter((c) => c.id !== courseId)
        }))
    }
});

To remove we are just taking the courseId, and by using the filter method getting all the courses that don’t have the provided Id.

Let’s also manage the state of Course Status,

const courseStore = (set) => ({
  courses: [],
  addCourse: (course) => {
    set((state) => ({
      courses: [course, ...state.courses],
    }));
  },
  removeCourse: (courseId) => {
    set((state) => ({
      courses: state.courses.filter((c) => c.id !== courseId),
    }));
  },
  toggleCourseStatus: (courseId) => {
    set((state) => ({
      courses: state.courses.map((course) =>
        course.id === courseIdf
          ? { ...course, completed: !course.completed }
          : course
      ),
    }));
  },
});

Here, we are again getting the courseId, mapping with the previous courses, and checking if the particular course exists. if it’s true then update the course completion status as !completed, else just return the course.

Now, let’s use the store. Till now basically, we’ve just created a structure for our state management. We haven’t used it yet, to use it we’ll use the Zustand methods.

const useCourseStore = create(
  devtools(
    persist(courseStore, {
      name: "courses",
    })
  )
);

export default useCourseStore;

Here, the useCourseStore is coming from Zustand. Now using the create method and here we’ll use devtools, and persist will store the data in local storage of the particular store, and we’ll name it “courses”.

Finally, we’ll export the useCourseStore, so that we can use it in action.

Also, check out,
Frontegg Tutorial for beginners using React

#2 Adding Courses to Store ⇒


Inside CourseForm.jsx, add below code.

Now, here we’ll use some basic React to create a Form and add some course using useState hook.

import React, { useState } from "react";

const CourseForm = () => {
  const [courseTitle, setcourseTitle] = useState("");

  const handleCourseSubmit = () => {
		//code
  };

  return (
    <div className="form-container">
      <input
        value={courseTitle}
        onChange={(e) => setcourseTitle(e.target.value)}
        className="form-input"
      />
      <button
        onClick={() => {
          handleCourseSubmit();
        }}
        className="form-submit-btn"
      >
        Add Course
      </button>
    </div>
  );
};

export default CourseForm;

Now, let’s import the useCourseStore, and use it to add course.

import useCourseStore from "../app/courseStore";

Using it is pretty simple,

const CourseForm = () => {
	const addCourse = useCourseStore((state) => state.addCourse);
  const [courseTitle, setcourseTitle] = useState("");

  const handleCourseSubmit = () => {
		if (!courseTitle) return alert("please add a course title");
    addCourse({
      id: Math.ceil(Math.random() * 1000000),
      title: courseTitle,
    });
    setcourseTitle("")
  };

fist, we get the addCourse, from the state that we’ve created. And in the handleCourseSubmit, we’ll use addCourse, to pass the Id and course Title, and that’s it. We’ve just added a course to the store.

After hitting Add course button, check the local store of the browser, and you’ll find the added course.

Zustand tutorial React

#3 Pulling Courses from Store and Manipulating it ⇒

In CourseList.jsx, add below code.

Now, let’s show all the courses that we’ve added.

import React from "react";
import useCourseStore from "../app/courseStore";

const CourseList = () => {
  const { courses, removeCourse, toggleCourseStatus } = useCourseStore(
    (state) => ({
      courses: state.courses,
      removeCourse: state.removeCourse,
      toggleCourseStatus: state.toggleCourseStatus,
    })
  );
return (<>
	//code
</>)
}

To do that, we are required to get the methods that we declared in courseStore.js, and we can get that from useCourseStore. Here we are just returning all the methods and their current states.

Now, once we have all the states, let’s use them in action.

import React from "react";
import useCourseStore from "../app/courseStore";

const CourseList = () => {
  const { courses, removeCourse, toggleCourseStatus } = useCourseStore(
    (state) => ({
      courses: state.courses,
      removeCourse: state.removeCourse,
      toggleCourseStatus: state.toggleCourseStatus,
    })
  );
  return (
    <>
      <ul>
        {courses.map((course, i) => {
          return (
            <React.Fragment key={i}>
              <li
                className="course-item"
                style={{
                  backgroundColor: course.completed ? "#00FF0044" : "white",
                }}
              >
                <span className="course-item-col-1">
                  <input
                    checked={course.completed}
                    type="checkbox"
                    onChange={(e) => {
                      toggleCourseStatus(course.id);
                    }}
                  />
                </span>
                <span>{course?.title}</span>
                <button
                  onClick={() => {
                    removeCourse(course.id);
                  }}
                  className="delete-btn"
                >
                  Delete
                </button>
              </li>
            </React.Fragment>
          );
        })}
      </ul>
    </>
  );
};

export default CourseList;

Now, we are able to remove courses, toggle the course status and display all of the courses. Also, if you refresh the browser, you can notice that all the data is still there because it’s stored in local storage.

#4 Add the Styles ⇒

Inside App.css, add below code.

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

ul {
  padding: 0;
}

.main-container {
  padding: 2rem;
  height: 100vh;
  color: #fff;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
}

.form-container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
}

.form-input {
  padding: 0.75rem;
  outline: none;
  border: 3px solid #666;
  width: 18rem;
}

.form-submit-btn {
  background-color: #666;
  padding: 0.75rem;
  border: 1px solid #666;
  color: #fff;
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
}

.course-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 24rem;
  box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
  border-radius: 10px;
  border: 2px solid white;
  margin: 1rem 0;
  padding: 1rem;
  color: black;
  font-weight: bold;
}

.course-item-col-1 {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  gap: 5px;
}

.delete-btn {
  padding: 0.5rem;
  color: black;
  background-color: transparent;
  border: 1px solid black;
  border-radius: 5px;
  cursor: pointer;
}

Check here for the complete source code on GitHub

That’s how simple and precise it is to manage the state in Zustand. I hope this post was helpful, if it was, share and let us know in the comments what would you like to learn in the next post.

Spread the love
```html ```

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published.