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
Explore My Other Channel for More Cool and Valuable Insights
👉 Youtube Learn Tech Tips👉 Tiktok
👉 Facebook: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.
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
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()}>
×
</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 ♬ 喜气洋洋迎新年-巧克力盒子 - 巧克力盒子