Building a Simple Todo List with Vite, Material-UI, and Zustand
Devesh Lashkari
Crafting Scalable Web & Mobile Solutions at Metafic | React | Next.js | Node.js | React Native | Firebase | AWS
If you have ever worked on React applications, you may have used any state management library to manage the state across all your components. React comes preloaded with built-in state management which is very good if your app is small, but when it comes to large-scale applications, it is essential to use a state management library that will help you to manage the state more efficiently.
Today we will be using one of the state management libraries called Zustand. Zustand is a state management library that will help you to manage the state easily and effectively. It is fast, lightweight and easy to integrate.
We will be creating a simple Todo Application which will use Vite, Material-UI V6, and Zustand to understand how to work with Zustand.
Before we start, let's first understand what Zustand is
Zustand is a small, fast and scalable state-management library. It is easy to use and provides an effortless?API compared to other state management libraries like Redux, Recoil, Mobx etc.
Why should you use Zustand??
Let’s get started with Zustand
Once you are done, open your project in VS Code and install the dependencies
Write the following command to check if our newly created Vite app is working or not
Once you run it, open https://localhost:5173 in your browser to see if your project is running.
Now, let us install all the dependencies
MUI needs some other dependencies to run, If you want to check you can refer to the documentation here: https://mui.com/material-ui/getting-started/installation/
Setting up Zustand for managing the state
In your src folder, create a new folder called store. Inside the store folder create a new file called todoListStore.js
This file will have all the Zustand code. We will export it and use it in our JSX components.
Let's understand the code we have written:
On line 10, we have used Math.random().toString(36).substr(2, 9), which generates a random string consisting of 9 alphanumeric characters, ensuring a unique ID for each todo.
领英推荐
Working on TodoList UI
Go to the src folder and create a new folder called components. Inside components create a new file called TodoList.jsx
Now add the TodoList component to our Main component i.e., App.jsx?
Write the following code inside TodoList component we have just created
import {
Box,
Button,
TextField,
Typography,
Container,
List,
ListItem,
ListItemText,
IconButton,
} from "@mui/material";
import CheckIcon from "@mui/icons-material/Check";
import DeleteIcon from "@mui/icons-material/Delete";
import React from "react";
import useTodoStore from "../store/todoListStore";
const TodoList = () => {
const [inputValue, setInputValue] = React.useState("");
const { todos, addTodo, removeTodo, toggleTodo } = useTodoStore();
const handleAddTodo = () => {
if (inputValue.trim()) {
addTodo(inputValue);
setInputValue("");
}
};
return (
<>
<Container
maxWidth="sm"
style={{ textAlign: "center", marginTop: "50px" }}
>
<Typography variant="h1" gutterBottom>
Todo
</Typography>
<Box
component="form"
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
gap: 2,
}}
>
<TextField
label="Add a todo"
variant="outlined"
sx={{ width: "500px" }}
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<Button variant="contained" color="primary" onClick={handleAddTodo}>
Add
</Button>
</Box>
<List>
{todos.length > 0 ? (
todos.map((_data, index) => (
<ListItem
key={index}
secondaryAction={
<>
<IconButton
edge="end"
aria-label="complete"
onClick={() => toggleTodo(_data.id)}
>
<CheckIcon
color={_data.completed ? "success" : "default"}
/>
</IconButton>
<IconButton
edge="end"
aria-label="delete"
onClick={() => removeTodo(_data.id)}
>
<DeleteIcon color="error" />
</IconButton>
</>
}
>
<ListItemText
primary={
<span
style={{
textDecoration: _data.isCompleted
? "line-through"
: "none",
}}
>
{_data.text}
</span>
}
/>
</ListItem>
))
) : (
<Typography variant="body1" align="center" color="textSecondary">
No todos yet. Add one above!
</Typography>
)}
</List>
</Container>
</>
);
};
export default TodoList;
Component Breakdown
The component imports essential modules from React, MUI components, and icons for building UI. It also imports the Zustand store (useTodoStore), which manages the todo state.
Using Zustand, the TodoList component accesses the global state for todos. It extracts the todos, addTodo, removeTodo, and toggleTodo functions from the store.?
A local state variable inputValue is created using useState to manage the input field's value. This allows users to enter new todos.
The handleAddTodo function is responsible for adding a new todo. It checks if the input value is not empty and then calls the addTodo function from the store, passing the input value. After adding, it resets the input field to an empty string.
The todos are rendered in a List component. If there are todos, it maps over the todos array and creates a ListItem for each one:
Each ListItem includes:
Time to test our app
Go to the terminal and write
You should now be able to see our fully functional Todo App, in which you can:
This is the final look of our Todo List Application
If you want the code, here is the link to the Github repo: github.com/deveshlashkari/todo-list-zustand
Conclusion
In this article, we have created a Todo List using Material-UI for styling and Zustand for state management.?
Material-UI is an excellent choice for quickly building beautiful user interfaces in React. It offers a wide range of pre-designed components that follow Material Design principles, making it easy to create beautiful and responsive applications without starting from scratch.
Zustand stands out for its simplicity and efficiency, making it an ideal option for handling state in small to medium-sized applications. Unlike other solutions like Redux, it cuts down on the boilerplate, allowing you to write less code.