Building a Simple Todo List with Vite, Material-UI, and Zustand

Building a Simple Todo List with Vite, Material-UI, and Zustand

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??

  • Zero Boilerplate: Unlike Redux, Zustand eliminates the need for reducers, actions, or middleware, making state management simpler and more efficient.
  • Ease of Use: Zustand offers an intuitive and minimal API, making state management straightforward.
  • Optimized Performance: Zustand ensures that only components relying on the updated state are re-rendered, improving performance.


Let’s get started with Zustand

  • Create a new project in React (using create-react-app) or Vite. We will be using Vite for this project.



create react app using vite

  1. Select React under Select a Framework?
  2. Select JavaScript under Select a variant


Once you are done, open your project in VS Code and install the dependencies


dependencies installation

Write the following command to check if our newly created Vite app is working or not


run vite

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

  1. Material-UI (@mui/material @emotion/react @emotion/styled @mui/icons-material)
  2. Zustand


install 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.



zustand store

Let's understand the code we have written:

  • On Line 4, we have an initial state called todos.?
  • addTodo: When called, the function will add an object with id, text and isCompleted flag.

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.

  • removeTodo: This function will remove the todo from the existing state by comparing the id which will be passed as a parameter.
  • toggleTodo: This function will toggle the isCompleted flag using id.?


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?


import TodoList in 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

  • Imports:

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.

  • State Management:

Using Zustand, the TodoList component accesses the global state for todos. It extracts the todos, addTodo, removeTodo, and toggleTodo functions from the store.?

  • Local State:

A local state variable inputValue is created using useState to manage the input field's value. This allows users to enter new todos.

  • Adding 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.

  • Displaying Todos:

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:

  1. An icon button to toggle completion (CheckIcon).
  2. An icon button to remove the todo (DeleteIcon).
  3. The todo text gets a strikethrough style if marked as completed.
  4. If there are no todos, a message prompts users to add one.

Time to test our app

Go to the terminal and write


npm run

You should now be able to see our fully functional Todo App, in which you can:

  1. Add a todo
  2. Remove a todo?
  3. Change the todo state to completed


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.



要查看或添加评论,请登录

Devesh Lashkari的更多文章

社区洞察

其他会员也浏览了