Did you ever wanted to have a simple, easy, efficient and lovely state-management solution for your react projects ? then search no more because today i'll show you a pretty neat technology that includes all those attributes. Are you exited ? Let's get started!
What is Zustand
from the creator of Zustand pmndrs:
Zustand is 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.
Check out the repo to learn more
You will see from the example below that Zustand is indeed a small, fast and scalable bearbones state-management solution.
Why Zustand over Redux/Context-API ?
- Zustand is simple and not opinionated
- Doesn't wrap your app in a provider
- Utilizes hooks for handling state
- No configuration needed
Basic usage
We will create a counter app as an example
- Install the dependency
npm i zustand
- Create a store
// store/store.js
import create from 'zustand'
export const useStore = create(set => ({
count: 0,
incrementCount: () => set(state => ({count: state.count + 1})),
incrementCount: () => set(state => ({count: state.count - 1}))
}))
Note
create
is a special function that creates a state object with the props we defined
set
is a special function that merges the state.The curly braces inside parenthesis
() => ({})
tells javascript that we are returning an object instead of it being a code block
- Use the hook inside your component
import {useStore} from "./store/store.js"
function MyComponent() {
// you can destrcture the state
// const count = useStore({count} => count)
const count = useStore(state => state.count)
const incrementCount = useStore(state => state.decrementCount)
const incrementCount = useStore(state => state.decrementCount)
return
<div>
<p>Count: {count}</p>
<button onClick={() => incrementCount}>Increment</button>
<button onClick={() => incrementCount}>Decrement</button>
</div>
}
And voilà that's it! simple right ?
Zustand Todo App
We will create the ole Todo app to demonstrate how it easy to work with Zustand
The purpose of this article is just to show how to use Zustand so i'll keep the app simple and not include check, rename or delete functionality for the todos
- Open the terminal, Create a react app and navigate to it
npx create-react-app zustand-example && cd zustand-example
After the installation has been finished we will create a simple form with an input and submit button, So go ahead and type:
import styles from "./App.module.css";
import {useState} from "react"
function App() {
const handleSubmit = (e) => {
e.preventDefault()
}
return (
<div className={styles.App}>
<form onSubmit={handleSubmit} className={styles.form}>
<input value={value} onChange={(e) => setValue(e.currentTarget.value)} className={styles.input} placeholder="Add a new todo" />
<button className={styles.button}>Add</button>
</form>
</div>
);
}
export default App;
As you can see this is a generic form with a controlled input, Here is how our form looks like:
!
This is the styles if you're wondering
.App {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background: whitesmoke;
}
.form {
width: 30%;
height: 80%;
background: white;
border-radius: 12px;
padding: 2rem;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 0 5px black;
}
.input {
width: 80%;
padding: 10px 12px;
}
.button {
color: white;
background: aqua;
padding: 10px 20px;
margin: 20px 0;
border: none;
width: 140px;
}
We will now implement Zustand
- first install the dependency
npm i zustand
- inside the
src
folder create a folderstore
withstore.js
in it
Inside the store object we will create todos
property which is an array and addTodo
method to push a new todo to the array
import create from "zustand";
export const useStore = create((set) => ({
todos: [],
addTodo: (todo) =>
set((state) => ({
todos: [...state.todos, todo],
})),
}));
We're pretty much done here, Now we need to add logic to our form
- Import
useStore
hook and call it
import {useStore} from "./store/store"
function App() {
const todos = useStore((state) => state.todos);
const addTodo = useStore((state) => state.addTodo);
}
- Inside
handleSubmit
function we will submit a new todo to our todos array
const handleSubmit = (e) => {
e.preventDefault()
addTodo(value);
}
And finally we will map the todos
array to represent the todos
return (
{todos.map((todo) => {
return (
<ul>
<li>{todo}</li>
</ul>
);
})}
)
And that's it!, Lets test our app
Full code:
import { useState } from "react";
import styles from "./App.module.css";
import { useStore } from "./store/store";
function App() {
const todos = useStore((state) => state.todos);
const addTodo = useStore((state) => state.addTodo);
const [value, setValue] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
addTodo(value);
setValue("");
};
return (
<div className={styles.App}>
<form onSubmit={handleSubmit} className={styles.form}>
<input
value={value}
onChange={(e) => setValue(e.currentTarget.value)}
className={styles.input}
placeholder="Add a new todo"
/>
<button className={styles.button}>Add</button>
{todos.map((todo) => {
return (
<ul>
<li>{todo}</li>
</ul>
);
})}
</form>
</div>
);
}
export default App;
Typescript
If you're using typescript you can define the store types with interface
import create from "zustand";
interface IStore {
todos: string[];
addTodo: (todo: string) => string
}
export const useStore = create<IStore>((set) => ({
todos: [],
addTodo: (todo) =>
set((state) => ({
todos: [...state.todos, todo],
})),
}));
Conclusion
We've reached the end of this post hopefully it encourages you to use Zustand :). If you like this post you can follow me for more, Thanks for reading, Happy state-managing!
Top comments (2)
Curious to know: have you ever used Zustand with a complex project ? How did it go ? Are there other state-management tools do you prefer to use over Zustand ?
Please fix your code