Background
Break News
How to add local font to Tailwind Css and NextJS? - Tutorial Design Pattern? - Blockchain Technology, How to create own Bitcoin virtual currency - Zustand mordern management state - Design Pattern - Flyweight Pattern? - Docker Full training Topic

[Tips] "Zustand Simple, modern state management for React"

Thursday, 19 January 2023
|
Read: Completed in minutes

[Tips] "Zustand Simple, modern state management for React"

Hello everyone, welcome to Learn Tech Tips Zidane 

If you working with React, you will see we will work with state, and if you have many state, you should use state management for React for you can easy control it by yourself. This topic today I will share with you 

The simple modern state management for React, That is Zustand

Zustand simple modern state management for React. Actually, the title is had share with your more information. On this topic I don't focus compare another manage state tool, I just focus on Zustand, About compare state. I will show more information on another topic

On This topic I will share with you detail source code, guideline for how to manage state with Zustand

Before go to management state. Here is some information about Zustand

Zustand is a fast and scalable state management solution built by the developers of Jotai and React springs. Zustand is known for its simplicity, using hooks to manage states without boilerplate code.

There are several popular React State Management options available, but the following are some justifications for choosing Zustand:

1. Zustand makes use of simple-to-use and easily implementable code.

2. Zustand renders components only on changes to the value of the state. Changes in the state can often be handled without having to re-render a component.

3. Less boilerplate code

4, Uses hooks to consume states. Hooks are popular in React and, as such, a welcome state management method.

5. State management is centralized and updated via simple defined actions. It is similar to  Redux in this regard, but unlike Redux, where the developer has to create reducers, actions, and dispatch to handle state, Zustand makes it far easier.

6. Provides clean code by eliminating the need to use Context Provides, thus resulting in shorter and more readable code.

7. Zustand provides a middleware to view state values from browser using Redux dev tool extension.

8.  Zustand makes storing asynchronous data in the very easy

Simple, modern state management for React

Zustand Package need install


       npm install zustand / yarn install zustand 

Create store in Zustand


const useStore = create(set => ({
    number: 0,  /* type: number*/
    fruits: [],  /* type: array */
})) 


Debug state with Redux dev tool extension

const store = (set, get) => ({

        number: 0,

        addNumber: (item: IState)  => {

               set(state => ({

                           todos: [...state.todos, item]

               }))

       }

})
const useStore = create(devtools(store))

Now you can view of the state using devtool

"Zustand Simple, modern state management for React" - Webzone Tech Tips Zidane

Working with asynchronous data very easy


  const url = "https://api.github.com/search/users?q=zidane&per_page=10";
  const useStore = create((set) => ({
    url: url,
    Results: {},
    fetch: async (url) => {
      const response = await fetch(url)
      const json = await response.json();
      set({ Results: json.items })
    },
  }))

When create store with Zustand, you just simple create one store.js file. 

Example a todos state management a state for set, get todos tasks everyday, addTodo function, function markCompleteTodo function, remove Todo function

first take a look my current package for make sure version and package name need to install

package.json

{
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "moment": "^2.29.4",
    "next": "latest",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "zustand": "^4.3.1"
  },
  "devDependencies": {
    "@types/node": "18.11.3",
    "@types/react": "18.0.21",
    "@types/react-dom": "18.0.6",
    "autoprefixer": "^10.4.12",
    "postcss": "^8.4.18",
    "tailwindcss": "^3.2.4",
    "typescript": "4.9.4"
  }
} 
 	 

store/todo/index.js

 
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import { IItem } from './../../utils/interface'
import { STATUS } from './../../utils/contants'
import moment from 'moment';

interface ITodoState {
    id: string;
    job: string;
    type: string; 
    remark: string;
    dueDate: Date;
    status: STATUS.DOING;
    created: Date;
}

interface IListTodoState {
    todos: Array<ITodoState>
    types: Array<IItem>
    addTodo: () => void;
    markCompleteTodo: (id: number) => void;
    removeTodo: (id: number) => void;
}
  
const store = (set, get) => ({
    todos: [],
    types: [
        {'name': 'Home', 'value': 'Home'},
        {'name': 'Research', 'value': 'Research'},
    ],

    addTodo: (item: ITodoState) => {
        
        item.status = STATUS.DOING
        item.created = moment().format('YYYY-MM-DD')
        set(state => ({
            todos: [...state.todos, item]
        }))        
    },

    markCompleteTodo: (index: number) => {

        let items = get().todos;

        let temp = { ...items[index] };

        if (temp.status === STATUS.COMPLETED) {
            temp.status = STATUS.DOING
        } else {
            temp.status = STATUS.COMPLETED
        }
        
        items[index] = temp;

        set(state => ({
            todos: items
        })) 
    },

    removeTodo: (id: string) => {
        let items = get().todos

        items = items.filter( (item, _) => {
            return item.id != id
        }) 

        set(state => ({
            todos: items
        }))
    }
})
const useStore = create<IListTodoState>(devtools(store))

export default useStore
 	

page/todos/index.ts

import { useEffect, useRef } from "react";

import useTodoStore from "./../../store/todos";

import TDHeader from "./../../components/TDHeader";
import TDFooter from "./../../components/TDFooter";
import TDTitle from "./../../components/TDTitle";
import TDCombobox from "./../../components/TDCombobox";
import {
  TDEditIcon,
  TDRemoveIcon,
  TDMarkCompleteIcon,
  TDMarkUncompleteIcon,
} from "./../../components/TDIcon";

import moment from "moment";

import { STATUS } from "./../../utils/contants";

export default function Add() {
  const { todos, types, addTodo, removeTodo, markCompleteTodo } =
    useTodoStore();
  const today = moment().format("YYYY-MM-DD");

  const id = useRef();
  const job = useRef();
  const dueDate = useRef();
  const remark = useRef();


  useEffect(() => {
    let modal = document.getElementById("addModal");
    modal.style.display = "none";
  }, [])    // call 1 time

  const closeAddForm = () => {
    let modal = document.getElementById("addModal");    
    modal.style.display = "none";  
  };

  const openAddForm = () => {
    let modal = document.getElementById("addModal");
    modal.style.display = "block";
  };

  const add = () => {
    let item = {
      id: id.current.value,
      job: job.current.value,
      type: document.getElementById("Type").value,
      dueDate: dueDate.current.value,
      remark: remark.current.value,
    };
    addTodo(item);
  };

  const markCompleteFunc = (index: number) => {
    markCompleteTodo(index);
  };

  return (
    <>
      <TDHeader />
      <TDTitle>List Task items</TDTitle>
      <div className="mt-[10px] container mx-auto">
       
        <div className="flex justify-end">
            <button id="myBtn " onClick={() => openAddForm()}>
                Add New Task
            </button>
        </div>
        <div className="w-full">
          {todos.length > 0 && (
            <table className="table">
              <thead>
                <tr>
                  <th> No </th>
                  <th> Job </th>
                  <th> Type </th>
                  <th> Remark </th>
                  <th> Create Date </th>
                  <th> Due Date </th>
                  <th> Overdue </th>
                  <th> Status </th>
                  <th> Action </th>
                </tr>
              </thead>
              <tbody>
                {todos.map((item, index) => {
                  let dueDate = moment(item.dueDate, "YYYY-MM-DD");
                  let distance = moment.duration(dueDate.diff(today)).asDays();
                  let highlightOverDue = "";

                  if (distance === 0) {
                    if (item.status === STATUS.DOING) {
                      item.status = STATUS.OVERDUE;
                      highlightOverDue = "text-rose-700";
                    }
                  }

                  let todoStyle = "uncomplete";
                  let buttonCompleteStyle = "info";
                  let statusStyle = "doing";
                  if (item.status === STATUS.COMPLETED) {
                    todoStyle = "complete";
                    statusStyle = "done";
                    buttonCompleteStyle = "success";
                  }

                  let typeStyle = "home";
                  if (item.type === "Research") {
                    typeStyle = "research";
                  }

                  return (
                    <tr key={index} className={todoStyle}>
                      <td> {item.id} </td>
                      <td> {item.job} </td>
                      <td>
                        {" "}
                        <span className={typeStyle}> </span> {item.type}{" "}
                      </td>
                      <td> {item.remark} </td>
                      <td> {item.created} </td>
                      <td> {item.dueDate} </td>
                      <td>
                        {" "}
                        <span className={highlightOverDue}>
                          {" "}
                          {distance}{" "}
                        </span>{" "}
                        days!{" "}
                      </td>
                      <td className={statusStyle}> {item.status} </td>
                      <td>
                        <div className="flex space-x-2">
                          <button className="warning ">
                            <TDEditIcon bgColor="#FFF" width="20" />
                          </button>

                          <button
                            className="danger"
                            onClick={() => removeTodo(item.id)}
                          >
                            <TDRemoveIcon bgColor="#FFF" width="20" />
                          </button>

                          <button
                            className={buttonCompleteStyle}
                            onClick={() => markCompleteFunc(index)}
                          >
                            {item.status === STATUS.COMPLETED ? (
                              <TDMarkUncompleteIcon bgColor="#FFF" width="20" />
                            ) : (
                              <TDMarkCompleteIcon bgColor="#FFF" width="20" />
                            )}
                          </button>
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          )}

          {
          todos.length <= 0 &&
            <div className='text-rose-500 text-xl text-center h-[calc(100%-100px)]'> Congratulation, You had completed all task today!</div>
          }
        </div>


        <div id="addModal" className="modal">
            <div className="modal-content" id="modal-content">
              <div className="modal-header">
                  <h2>Add Items</h2>
                  <span className="close" onClick={() => closeAddForm()}>
                  &times;
                  </span>
              </div>
              <div className="modal-body">
                  <div className="bg-sky-300 shadow-lg p-4 container w-[500px] mx-auto rounded-md">
                  <div>
                      <label>
                      {" "}
                      <span className="required-star"> * </span> ID:{" "}
                      </label>
                      <input
                      ref={id}
                      required
                      type="text"
                      name="id"
                      placeholder="1"
                      />
                  </div>

                  <div>
                      <label>
                      {" "}
                      <span className="required-star"> * </span> Job Name:{" "}
                      </label>
                      <input
                      ref={job}
                      required
                      type="text"
                      name="job"
                      placeholder="Learn NextJS in 3 weeks"
                      />
                  </div>
                  <div className="mt-4">
                      <TDCombobox
                      is_required={true}
                      label="Type"
                      placeHolder="Please Select"
                      items={types}
                      />
                  </div>
                  <div className="mt-4">
                      <label>
                      {" "}
                      <span className="required-star"> * </span> Due Date:{" "}
                      </label>
                      <input
                      ref={dueDate}
                      min={today}
                      required
                      type="date"
                      name="dueDate"
                      />
                  </div>
                  <div className="mt-4">
                      <label> Remark: </label>
                      <input
                      ref={remark}
                      type="text"
                      name="text"
                      placeholder="Please focus, Try your best, go ahead"
                      />
                  </div>

                  <div className="mt-4 text-right">
                      <button onClick={add}> Add Todos Items </button>
                  </div>
                  </div>
              </div>
            </div>
        </div>

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

ultis/constants.ts

 export const ROUTES = {
    HOME: "/",
    TODO_LIST: "/todos/",
    TODO_ADD: "/todos/add",  
};

export const TYPE = {
  RESEARCH: 1,
  HOME: 2
}

export const STATUS = {
  DOING: 'DOING',
  COMPLETED: 'COMPLETED',
  OVERDUE: 'OVERDUE',    
}

 	
    

ultis/interface.ts 

  
export interface IItem {
    name: string;
    value: string;
}  	

Above step will help you easy use zustand state for manage todolist app. On next topic I will share with you how to build it

You can reference my source code on github.

https://github.com/zidane168/nextjs-zustand-todo

or on my tiktok channel


@learntechtips Zustand simple modern state management for REACT #zustand #learnontiktok #learntechtips #learntechtipszidane ♬ 喜气洋洋迎新年-巧克力盒子 - 巧克力盒子
Thanks for reading. Any feedback and questions abouve Zustand simple modern state management for React. Leave your comment on below post, we can discuss about it.
✋✋✋✋ Webzone Tech Tips - I am Zidane, See you next time

🙇🏼🙇🏼 We Appreciate Your Comments and Suggestions - Webzone, all things Tech Tips web development
Popular Webzone Tech Tips topic maybe you will be like it - by Webzone Tech Tips - Zidane
As a student, I found Blogspot very useful when I joined in 2014. I have been a developer for years . To give back and share what I learned, I started Webzone, a blog with tech tips. You can also search for tech tips zidane on Google and find my helpful posts. Love you all,

I am glad you visited my blog. I hope you find it useful for learning tech tips and webzone tricks. If you have any technical issues, feel free to browse my posts and see if they can help you solve them. You can also leave a comment or contact me if you need more assistance. Here is my blog address: https://learn-tech-tips.blogspot.com.

My blog where I share my passion for web development, webzone design, and tech tips. You will find tutorials on how to build websites from scratch, using hot trends frameworks like nestjs, nextjs, cakephp, devops, docker, and more. You will also learn how to fix common bugs on development, like a mini stackoverflow. Plus, you will discover how to easily learn programming languages such as PHP (CAKEPHP, LARAVEL), C#, C++, Web(HTML, CSS, javascript), and other useful things like Office (Excel, Photoshop). I hope you enjoy my blog and find it helpful for your projects. :)

Thanks and Best Regards!
Follow me on Tiktok @learntechtips and send me a direct message. I will be happy to chat with you.
Webzone - Zidane (huuvi168@gmail.com)
I'm developer, I like code, I like to learn new technology and want to be friend with people for learn each other
I'm a developer who loves coding, learning new technologies, and making friends with people who share the same passion. I have been a full stack developer since 2015, with more than years of experience in web development.
Copyright @2022(November) Version 1.0.0 - By Webzone, all things Tech Tips for Web Development Zidane
https://learn-tech-tips.blogspot.com