react mui 5 to-do app example

react mui 5 to-do app example

April 24, 2023 By Aaronn

In this tutorial, we will create to do app list in react with material ui (mui 5). We will see to-do app with useState hook, to-do app using typescript example with react material UI 5.

Install & Setup Vite + React + Typescript + MUI 5


React Material UI 5 To Do App Example

1. react mui 5 simple to-do app using useState hook. You can also refactor code by split code page like TodoApp.js TodoForm.js, TodoList.js.

import { useState } from 'react';
import { Box, Paper, TextField, Checkbox, Typography, Button } from '@mui/material';

const TodoItem = ({ todo, onToggle, onDelete }) => {
  const { id, text, completed } = todo;

  return (
    <Box display="flex" alignItems="center" marginY="8px">
      <Checkbox checked={completed} onChange={() => onToggle(id)} />
      <Typography variant="body1" sx={{ flexGrow: 1, mr: '16px', textDecoration: completed ? 'line-through' : 'none' }}>
        {text}
      </Typography>
      <Button variant="outlined" size="small" color="error" onClick={() => onDelete(id)}>
        Delete
      </Button>
    </Box>
  );
};

const TodoList = ({ todos, onToggle, onDelete }) => (
  <Box>
    {todos.map((todo) => (
      <TodoItem key={todo.id} todo={todo} onToggle={onToggle} onDelete={onDelete} />
    ))}
  </Box>
);

const TodoForm = ({ onSubmit }) => {
  const [text, setText] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();
    if (!text.trim()) return;
    onSubmit(text);
    setText('');
  };

  return (
    <Box
      component="form"
      onSubmit={handleSubmit}
      display="flex"
      alignItems="center"
      marginBottom="16px"
      sx={{ '& .MuiTextField-root': { flexGrow: 1, marginRight: '16px' } }}
    >
      <TextField value={text} onChange={(event) => setText(event.target.value)} label="New todo" variant="outlined" />
      <Button type="submit" variant="contained" color="primary">
        Add
      </Button>
    </Box>
  );
};

const TodoApp = () => {
  const [todos, setTodos] = useState([]);

  const handleAddTodo = (text) => {
    setTodos([...todos, { id: Date.now(), text, completed: false }]);
  };

  const handleToggleTodo = (id) => {
    setTodos((todos) =>
      todos.map((todo) =>
        todo.id === id
          ? {
              ...todo,
              completed: !todo.completed,
            }
          : todo
      )
    );
  };

  const handleDeleteTodo = (id) => {
    setTodos((todos) => todos.filter((todo) => todo.id !== id));
  };

  return (
    <Box maxWidth="600px" margin="0 auto" padding="16px">
      <Typography variant="h3" align="center" gutterBottom>
        Todo List
      </Typography>
      <Paper component={TodoForm} onSubmit={handleAddTodo} marginBottom="16px" />
      <Paper component={TodoList} todos={todos} onToggle={handleToggleTodo} onDelete={handleDeleteTodo} />
    </Box>
  );
};

export default TodoApp;
react mui 5 to do app

react mui 5 to do app


2. react mui 5 to-do app with typescript using useState hook.

import { useState } from "react";
import {
  Box,
  Paper,
  TextField,
  Checkbox,
  Typography,
  Button,
} from "@mui/material";

type Todo = {
  id: number;
  text: string;
  completed: boolean;
};

type TodoItemProps = {
  todo: Todo;
  onToggle: (id: number) => void;
  onDelete: (id: number) => void;
};

const TodoItem = ({ todo, onToggle, onDelete }: TodoItemProps) => {
  const { id, text, completed } = todo;

  return (
    <Box display="flex" alignItems="center" marginY="8px">
      <Checkbox checked={completed} onChange={() => onToggle(id)} />
      <Typography
        variant="body1"
        sx={{
          flexGrow: 1,
          mr: "16px",
          textDecoration: completed ? "line-through" : "none",
        }}
      >
        {text}
      </Typography>
      <Button
        variant="outlined"
        size="small"
        color="error"
        onClick={() => onDelete(id)}
      >
        Delete
      </Button>
    </Box>
  );
};

type TodoListProps = {
  todos: Todo[];
  onToggle: (id: number) => void;
  onDelete: (id: number) => void;
};

const TodoList = ({ todos, onToggle, onDelete }: TodoListProps) => (
  <Box>
    {todos.map((todo) => (
      <TodoItem
        key={todo.id}
        todo={todo}
        onToggle={onToggle}
        onDelete={onDelete}
      />
    ))}
  </Box>
);

type TodoFormProps = {
  onSubmit: (text: string) => void;
};

const TodoForm = ({ onSubmit }: TodoFormProps) => {
  const [text, setText] = useState("");

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (!text.trim()) return;
    onSubmit(text);
    setText("");
  };

  return (
    <Box
      component="form"
      onSubmit={handleSubmit}
      display="flex"
      alignItems="center"
      marginBottom="16px"
      sx={{ "& .MuiTextField-root": { flexGrow: 1, marginRight: "16px" } }}
    >
      <TextField
        value={text}
        onChange={(event) => setText(event.target.value)}
        label="New todo"
        variant="outlined"
      />
      <Button type="submit" variant="contained" color="primary">
        Add
      </Button>
    </Box>
  );
};

const TodoApp = () => {
  const [todos, setTodos] = useState<Todo[]>([]);

  const handleAddTodo = (text: string) => {
    setTodos([...todos, { id: Date.now(), text, completed: false }]);
  };

  const handleToggleTodo = (id: number) => {
    setTodos((todos) =>
      todos.map((todo) =>
        todo.id === id
          ? {
            ...todo,
            completed: !todo.completed,
          }
          : todo
      )
    );
  };

  const handleDeleteTodo = (id: number) => {
    setTodos((todos) => todos.filter((todo) => todo.id !== id));
  };

  return (
    <Box maxWidth="600px" margin="0 auto" padding="16px">
      <Typography variant="h4" align="center" gutterBottom>
        React + MUI 5 + TypeScript Todo List
      </Typography>
      <Paper
        component={TodoForm}
        onSubmit={handleAddTodo}
        marginBottom="16px"
      />
      <Paper
        component={TodoList}
        todos={todos}
        onToggle={handleToggleTodo}
        onDelete={handleDeleteTodo}
      />
    </Box>
  );
};

export default TodoApp;
react mui 5 to do app with typescript

react mui 5 to do app with typescript