What's up Folks!!
It's nice to have you here again!!!
This is the fifth and last part of articles of react management. How are you feeling with so much technologies? Amazing, isn’t it?
I hope you guys liked the previous article where I talk about state management using the Recoil. If you did not saw the previous part you can find here: Recoil State Management
We are going to create a simple application but we will be able to see the code of the technologies and compare the implementation.
Just to remember, let's get situated:
Summary:
Introduction
Zustand is comparable to Redux, as Jotai with Recoil.
According to the documentation of zustand:
A small, fast and scalable bearbones state-management solution using simplified flux principles. Has a comfy API based on hooks, isn't boilerplatey or opinionated.
Don't disregard it because it's cute. It has quite the claws, lots of time was spent dealing with common pitfalls, like the dreaded zombie child problem, react concurrency, and context loss between mixed renderers. It may be the one state-manager in the React space that gets all of these right.
Just to remember again (sorry but it's important), if you did not saw the first part, I created all components that we will use in this and next parts on the first part. Soooo if you did not saw yet, I recommend to see on: State Management on React [Part 1] - Context API.
We will use the first version of the components that we created without the Context API integration.
Now with all concepts at hands, LET'S TO THE CODE!
Show me the code
Let's add the dependencies that we will use:
yarn add zustand
easy peasy!
And now, we don't need to a provider on our main.txt
.
So I will create a directory on /src
called store
and for each feature I will create a folder with the name of the feature. So, we have two features in our app: change theme color (dark mode) and a todo list:
Let's start with the dark mode feature. We will create a file on store/theme/index.ts and create this code:
import { create } from "zustand";
export interface ITheme {
isDark: boolean;
changeTheme: VoidFunction;
}
export const useTheme = create<ITheme>((set) => ({
isDark: false,
changeTheme: () => {
set((state: ITheme) => ({ isDark: !state.isDark }));
},
}));
I'm using an interface to define our typess on our zustand hooks to manage the state. Zustand offers the create
function to we manage the state.
Now let's see how the todo store will it be.
Let's create on store/theme two files. First the types types.ts (my preference) and the index.ts.
The types.ts contains just the type of our todo:
store/todo/types.ts
export interface ITodo {
id: number;
text: string;
done: boolean;
}
export interface ITodoState {
todos: ITodo[];
addTodo: (text: string) => void;
removeTodo: (id: number) => void;
}
And creating the store for our todo list on index.ts and the code will be:
store/todo/index.ts
import { create } from "zustand";
import { ITodo, ITodoState } from "./types";
export const useTodo = create<ITodoState>((set) => ({
todos: [],
addTodo: (text: string) => {
const newTodo: ITodo = {
id: Math.random() * 1000,
text,
done: false,
};
set((state: ITodoState) => ({
todos: [...state.todos, newTodo],
}));
},
removeTodo: (id: number) => {
set((state: ITodoState) => ({
todos: state.todos.filter((todo) => todo.id !== id),
}));
},
}));
The create
function pass the set
parameter for we update the state. Now we can see the similarities with redux, when I introduce the slicers.
Now we will change the components to use our zustand state.
First, let's change the ButtonChangeTheme:
components/ButtonChangeTheme/index.tsx
import { ITheme, useTheme } from "../../store/theme";
import button from "./button.module.css";
interface ButtonChangeThemeProps {
label: string;
}
export const ButtonChangeTheme = ({ label }: ButtonChangeThemeProps) => {
const changeColor = useTheme((state: ITheme) => state.changeTheme);
return (
<button className={button.btn} onClick={changeColor}>
{label}
</button>
);
};
We just need to use the hook that we created and get the function that we created.
Now changing the Content
dummy component:
components/Content/index.tsx
import { ITheme, useTheme } from "../../store/theme";
interface ContentProps {
text: string;
}
export const Content = ({ text }: ContentProps) => {
const isDark = useTheme((state: ITheme) => state.isDark);
return (
<div
style={{
height: "30vh",
width: "100vw",
color: isDark ? "#fff" : "#111827",
backgroundColor: isDark ? "#111827" : "#fff",
}}
>
{text}
</div>
);
};
The same thing will happens on FormTodo and ListTodo:
components/FormTodo/index.tsx
import style from "./Form.module.css";
import { Button } from "../Button";
import { Input } from "../Input";
import { useState } from "react";
import { useTodo } from "../../store/todo";
import { ITodoState } from "../../store/todo/types";
export const FormTodo = () => {
const [todo, setTodo] = useState("");
const addTodo = useTodo((state: ITodoState) => state.addTodo);
const handleAddTodo = () => {
addTodo(todo);
setTodo("");
};
return (
<div className={style.formContainer}>
<Input
value={todo}
label="Todo"
onChange={(evt) => setTodo(evt.target.value)}
/>
<Button label="Adicionar" onClick={handleAddTodo} />
</div>
);
};
components/ListTodo/index.tsx
import { useTodo } from "../../store/todo";
import { ITodoState } from "../../store/todo/types";
import style from "./ListTodo.module.css";
export const ListTodo = () => {
const removeTodo = useTodo((state: ITodoState) => state.removeTodo);
const todos = useTodo((state: ITodoState) => state.todos);
return (
<ul>
{todos.map((todo) => (
<li className={style.item} key={todo.id}>
<label>{todo.text}</label>
<i className={style.removeIcon} onClick={() => removeTodo(todo.id)} />
</li>
))}
</ul>
);
};
And for the fourth time we finish the same app!
Results
Zustand has many similarities with Redux.
But what I introduced to you it's just the basic of Zustand. Read the documentation to see more details.
The visual will not change but let's see 👀 using the React Dev Tools with highlighting render:
And the React Dev Tools Profiler:
Conclusion
Zustand is offers a minimal API to manager our state. It simple and easy to maintain our state.
But different of Redux, zustand offers:
- Makes hooks the primary means of consuming state
- Doesn't wrap your app in context providers
- Less boilerplate
I'm not saying that zustand is the best library and that you should use it in all your project. Or that all the state managers that I introduce to you are the best.
Like everything in computing the answer is: it depends.
It depends of your project, product, solution, team, knowledges and a lot of more...
So much so that the purpose of these articles is to compare the application and implementation of each state manager.
So in summary, in this article we saw how we can implement the zustand and manipulate our global state.
It was a pleasure to bring this content to you. I hope I have somehow added some knowledge or brought some technology that can help you.
It's been a long journey, but we're done here. See you in some upcoming articles. Thank you very much.
Some references:
That's all folks!
Thank you so much!!!
Top comments (0)