DEV Community

Deepak Singh Kushwah
Deepak Singh Kushwah

Posted on

Get started with Zustand

Hello Developers

Today we are going to see how to use Zustand state manager with react.

Prerequisites

You should have some knowledge of ReactJS

Introduction

React is a great tool to create frontend apps. There are lots of tools available to manage state like Jotai, Redux, Recoil etc. There is one tool I like much is Zustand. It require minimum setup to get started. It follow very simple approach and today we are going through this.

Setup New Project

I am using PNPM & VITE to create and setup new project. You can use npm.
pnpm create vite@latest

Above command ask you to choose framework. Choose React and JavaScript in next step. Now we need to add zustand and other dependencies to our React app.

pnpm add zustand uuid react-icons
pnpm add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode

This will add Zustand to your project and install all dependencies.

Our file structure will be like this.

Image description

Very first, we will update the App.jsx file with following code.

import TodoForm from "./components/TodoForm";
import TodoList from "./components/TodoList";

function App() {
  return (
    <div className="App">
      <div className="flex">
        <div className="w-1/2">
          <TodoForm />
        </div>
        <div className="w-1/2">
          <TodoList />
        </div>
      </div>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now next part is to create store for our react app. Create new file in new "/store/todoStore.js" and put following code in file.

import { create } from "zustand";
import { v4 as uuidv4 } from "uuid";
const todoStore =(set, get) => ({
  todos: [],
  addTodo: (title) => {
    const newTodo = {
      title,
      id: uuidv4(),
      markDone: false,
    };
    set((state) => ({
      todos: [...state.todos, newTodo],
    }));
  },
  markTodoDone: (id, checked) => {
    let newTodos;
    if (checked === true) {
      newTodos = get().todos.map((todo) => {
        return todo.id === id ? { ...todo, markDone: true } : {...todo};
      });
    } else {
      newTodos = get().todos.map((todo) => {
        return todo.id === id ? { ...todo, markDone: false } : {...todo};
      });
    }

    set((state) => ({
      todos: newTodos,
    }));
  },
  removeTodo: (id) => {
    const newTodos = get().todos.filter((todo) => todo.id !== id);
    set(state => ({
      todos: newTodos
    }))
  },
});
const useTodoStore = create(todoStore);
export default useTodoStore;
Enter fullscreen mode Exit fullscreen mode

So we have created our store, now we use this store to manage our todos.

/components/TodoForm.jsx

import React from "react";
import { useState } from "react";
import useTodoStore from "../store/todoStore";
const TodoForm = () => {
  const [title, setTitle] = useState("");
  const addTodo = useTodoStore((state) => state.addTodo);
  const handleAddTodo = () => {
    addTodo(title);
    setTitle("");
  };
  return (
    <div>
      <h1>Todo Form</h1>
      <table className="border-2 w-full">
        <tbody>
          <tr>
            <td>
              <label htmlFor="todo">Todo</label>
            </td>
            <td>
              <input
                placeholder="Todo Text"
                type="text"
                className="border-red-500 border-2"
                name="todo"
                id="todo"
                onChange={(e) => setTitle(e.target.value)}
              />
            </td>
          </tr>
          <tr>
            <td>&nbsp;</td>
            <td>
              <button
                type="button"
                onClick={handleAddTodo}
                className="btn btn-primary"
              >
                Add Todo
              </button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  );
};

export default TodoForm;

Enter fullscreen mode Exit fullscreen mode

/components/TodoList.jsx

import React from "react";
import {BsTrash} from "react-icons/bs";
import useTodoStore from "../store/todoStore";
const TodoList = () => {
  const todos = useTodoStore((state) => state.todos);
  const markTodoDone = useTodoStore(state => state.markTodoDone);
  const removeTodo = useTodoStore(state => state.removeTodo);
  return (
    <div className="px-3">
      <h1>Todos</h1>
      {todos?.map((todo) => (
        <div className="flex justify-start items-center" key={todo.id}>
          <div className="w-auto">
            <input type="checkbox" name="markDone" onClick={(e) => markTodoDone(todo.id, e.target.checked)} id="" />
          </div>
          <div className="px-3"><BsTrash title="Remove Todo" className="cursor-pointer" onClick={() => removeTodo(todo.id)}/></div>
          <div className={`px-1 w-full ${todo.markDone === true ? 'line-through text-red-700' : ''}`}>{todo.title}</div>

        </div>
      ))}
    </div>
  );
};

export default TodoList;
Enter fullscreen mode Exit fullscreen mode

/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

.btn {
  @apply border-blue-500 bg-blue-800 text-white px-4 py-1 rounded-md;
}

input[type="text"]{
  @apply w-full px-4 py-1 rounded-md;
}
table {
  @apply border-2 border-red-300 shadow-lg w-full;
}
table td{
  @apply px-3 py-2; 
}

table tr:nth-child(even){
  @apply bg-gray-200;
}


table tr:nth-child(odd){
  @apply bg-white;
}
Enter fullscreen mode Exit fullscreen mode

/tailwindcss.config.cjs

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Enter fullscreen mode Exit fullscreen mode

Now you can run the project. You will see same screen if you have followed it here.

Image description

You can check the code on this link with using of persist middle-ware.

https://bitbucket.org/deepaksinghkushwah/dev-to-projects/src/main/react-zustand-example/

Happy Coding

Top comments (0)