bosconvertpro convert caseBosConvertPro

How to make simple todo list app in ReactTS

Abbos Nurgulshanov
10 February 2024
422 times viewed

I am going to make simple todo list app by using ReactTS. It has feature CRUD and all todos are saved to localstorage. So, I need eaxmple by using ReactTS and Radix for ui.

Answers
Abbos Nurgulshanov
10 February 2024

1. Create a new React project.

npx create-react-app todo-list



2. Add Radix to your project.

npm install @radix-ui/react



3. Create a new file called src/TodoItem.tsx.

import * as React from 'react'
import {Radix} from '@radix-ui/react'
import {CheckIcon, TrashIcon} from '@radix-ui/react-icons'

export interface TodoItemProps {
  todo: {
    id: string
    text: string
    completed: boolean
  }
  onToggle: (id: string) => void
  onDelete: (id: string) => void
}

const TodoItem: React.FC<TodoItemProps> = ({todo, onToggle, onDelete}) => {
  return (
    <Radix.Div className="flex items-center justify-between p-4 bg-gray-100 rounded-md shadow-sm">
      <label className="flex items-center">
        <Radix.Checkbox checked={todo.completed} onChange={(e) => onToggle(todo.id)}>
          <CheckIcon className="w-6 h-6 text-gray-500" />
        </Radix.Checkbox>
        <span className="ml-2 text-gray-700">{todo.text}</span>
      </label>
      <Radix.Button onClick={() => onDelete(todo.id)}>
        <TrashIcon className="w-6 h-6 text-gray-500" />
      </Radix.Button>
    </Radix.Div>
  )
}

export default TodoItem



4. Create a new file called src/TodoList.tsx.

import * as React from 'react'
import {Radix} from '@radix-ui/react'
import {PlusIcon} from '@radix-ui/react-icons'
import TodoItem from './TodoItem'

export interface TodoListProps {
  todos: {
    id: string
    text: string
    completed: boolean
  }[]
  onToggle: (id: string) => void
  onDelete: (id: string) => void
  onAdd: (text: string) => void
}

const TodoList: React.FC<TodoListProps> = ({todos, onToggle, onDelete, onAdd}) => {
  const [newTodoText, setNewTodoText] = React.useState('')

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    onAdd(newTodoText)
    setNewTodoText('')
  }

  return (
    <div className="max-w-md mx-auto">
      <h1 className="text-3xl font-bold mb-4">Todo List</h1>
      <form onSubmit={handleSubmit} className="flex items-center mb-4">
        <input
          type="text"
          placeholder="Add a new todo"
          value={newTodoText}
          onChange={(e) => setNewTodoText(e.target.value)}
          className="flex-1 p-2 border border-gray-300 rounded-md shadow-sm"
        />
        <Radix.Button type="submit" className="ml-2">
          <PlusIcon className="w-6 h-6 text-gray-500" />
        </Radix.Button>
      </form>
      <ul className="list-none">
        {todos.map((todo) => (
          <TodoItem key={todo.id} todo={todo} onToggle={onToggle} onDelete={onDelete} />
        ))}
      </ul>
    </div>
  )
}

export default TodoList




5. Create a new file called src/App.tsx.

import * as React from 'react'
import TodoList from './TodoList'

const App: React.FC = () => {
  const [todos, setTodos] = React.useState<
    {
      id: string
      text: string
      completed: boolean
    }[]
  >([])

  const onToggle = (id: string) => {
    setTodos((prevTodos) => {
      return prevTodos.map((todo) => {
        if (todo.id === id) {
          return {...todo, completed: !todo.completed}
        }
        return todo
      })
    })
  }

  const onDelete = (id: string) => {
    setTodos((prevTodos) => {
      return prevTodos.filter((todo) => todo.id !== id)
    })
  }

  const onAdd = (text: string) => {
    setTodos((prevTodos) => {
      return [
        ...prevTodos,
        {
          id: Math.random().toString(),
          text,
          completed: false,
        },
      ]
    })
  }

  return (
    <div className="App">
      <TodoList todos={todos} onToggle={onToggle} onDelete={onDelete} onAdd={onAdd} />
    </div>
  )
}

export default App



6. Start the development server.

npm start


7. Open your browser to http://localhost:3000.

You should see a todo list app that lets you add, delete, and toggle the completion status of todos.

Abbos Nurgulshanov
10 February 2024

1. Create a New React Project

npx create-react-app todo-app --template @typescript

 


2. Install Radix UI

npm install @radix-ui/react



3. Create a New Component for the Todo Item

   // TodoItem.tsx
   import { useState } from "react";
   import { Button, Checkbox, Container } from "@radix-ui/react";

   export const TodoItem = ({ todo, onDelete, onToggle }) => {
     const [checked, setChecked] = useState(todo.completed);

     const handleToggle = () => {
       setChecked(!checked);
       onToggle(todo.id);
     };

     const handleDelete = () => {
       onDelete(todo.id);
     };

     return (
       <Container>
         <Checkbox checked={checked} onChange={handleToggle} />
         <Container>
           {todo.text}
         </Container>
         <Button onClick={handleDelete}>Delete</Button>
       </Container>
     );
   };



4. Create a New Component for the Todo List

   // TodoList.tsx
   import { useEffect, useState } from "react";
   import { TodoItem } from "./TodoItem";

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

     useEffect(() => {
       const storedTodos = localStorage.getItem("todos");
       if (storedTodos) {
         const parsedTodos: Todo[] = JSON.parse(storedTodos);
         setTodos(parsedTodos);
       }
     }, []);

     useEffect(() => {
       localStorage.setItem("todos", JSON.stringify(todos));
     }, [todos]);

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

     const toggleTodo = (id: number) => {
       const updatedTodos = todos.map((todo) => {
         if (todo.id === id) {
           return {
             ...todo,
             completed: !todo.completed,
           };
         }
         return todo;
       });
       setTodos(updatedTodos);
     };

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

     return (
       <Container>
         <form onSubmit={(e) => {
           e.preventDefault();
           const text = (e.target as HTMLFormElement).querySelector(
             'input[name="todo"]'
           ) as HTMLInputElement;
           addTodo(text.value);
           text.value = "";
         }}>
           <label htmlFor="todo">Add a new todo:</label>
           <input type="text" name="todo" />
           <button type="submit">Add</button>
         </form>
         <ul>
           {todos.map((todo) => (
             <TodoItem
               key={todo.id}
               todo={todo}
               onDelete={deleteTodo}
               onToggle={toggleTodo}
             />
           ))}
         </ul>
       </Container>
     );
   };




5. Render the Todo List in the App Component

   import { TodoList } from "./TodoList";

   const App = () => {
     return (
       <div>
         <h1>Todo List</h1>
         <TodoList />
       </div>
     );
   };

   export default App;



6. Start the Development Server


npm start




7. Open your browser to http://localhost:3000

You should see a simple todo list app with a form for adding new todos and a list of existing todos. You can check off todos as you complete them and delete them by clicking the "Delete" button.

8. Additional Features

You can add additional features to your todo list app, such as:

* Drag and drop to reorder todos
* Dark mode
* Notifications for new todos
* Integration with a backend API

The possibilities are endless!

Abbos Nurgulshanov
10 February 2024

1. Create a new React application:



npx create-react-app todo-app-reactts



2. Install the necessary dependencies:



npm install @radix-ui/react @radix-ui/alpha radix reset-css



3. Create a new file called src/App.tsx:


import React, { useState } from 'react';
import {
  List,
  ListItem,
  ListItemButton,
  ListItemContent,
  ListItemText,
  Input,
  Button,
  Box,
} from '@radix-ui/react';
import { CloseIcon } from '@radix-ui/alpha';

const App = () => {
  const [todos, setTodos] = useState<string[]>([]);
  const [todo, setTodo] = useState('');

  const addTodo = () => {
    if (todo.trim()) {
      setTodos([...todos, todo]);
      setTodo('');
    }
  };

  const deleteTodo = (index: number) => {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  };

  return (
    <Box p={20}>
      <h1>Todo List</h1>
      <Input
        id="todo"
        value={todo}
        onChange={(e) => setTodo(e.target.value)}
        placeholder="Add a new todo..."
      />
      <Button onClick={addTodo}>Add Todo</Button>
      <List>
        {todos.map((todo, index) => (
          <ListItem key={index}>
            <ListItemButton onClick={() => deleteTodo(index)}>
              <CloseIcon />
            </ListItemButton>
            <ListItemContent>
              <ListItemText>{todo}</ListItemText>
            </ListItemContent>
          </ListItem>
        ))}
      </List>
    </Box>
  );
};

export default App;




4. Create a new file called src/index.css:


/* reset.css */

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  font-family: sans-serif;
}

a {
  text-decoration: none;
  color: inherit;
}

ul {
  list-style-type: none;
}

button {
  cursor: pointer;
}

/* app.css */

body {
  background-color: #f5f5f5;
}

h1 {
  margin-bottom: 20px;
}

input {
  width: 100%;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin-bottom: 10px;
}

button {
  padding: 10px 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #555;
  color: #fff;
}

ul {
  padding: 0;
}

li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px;
  border-bottom: 1px solid #ccc;
}

li:last-child {
  border-bottom: none;
}

.close-icon {
  cursor: pointer;
}



5. Start the development server:



npm start



6. Open your browser and navigate to http://localhost:3000 to see your todo list app in action!

Abbos Nurgulshanov
10 February 2024

1. Create a new React app.



npx create-react-app todo-app



2. Install the necessary dependencies.



npm install radix-ui



3. Create a new component for the todo list.



src/components/TodoList.tsx

 

import { useState, useEffect } from "react";
import { List, Item, Button } from "radix-ui";

const TodoList = () => {
  const [todos, setTodos] = useState<string[]>([]);

  useEffect(() => {
    const storedTodos = localStorage.getItem("todos");
    if (storedTodos) {
      setTodos(JSON.parse(storedTodos));
    }
  }, []);

  const addTodo = (todo: string) => {
    setTodos([...todos, todo]);
    localStorage.setItem("todos", JSON.stringify([...todos, todo]));
  };

  const deleteTodo = (index: number) => {
    const newTodos = todos.filter((_, i) => i !== index);
    setTodos(newTodos);
    localStorage.setItem("todos", JSON.stringify(newTodos));
  };

  return (
    <List>
      {todos.map((todo, index) => (
        <Item key={index}>
          <div>{todo}</div>
          <Button onClick={() => deleteTodo(index)}>Delete</Button>
        </Item>
      ))}
      <Button onClick={() => addTodo("New Todo")}>Add Todo</Button>
    </List>
  );
};

export default TodoList;





5. Start the development server.


npm start



6. Open your browser to http://localhost:3000 to see the todo list app.

Abbos Nurgulshanov
10 February 2024

1. Setup Project:
- Install React with TypeScript: npx create-react-app my-todo-app --template @typescript
- Install Radix: yarn add @radix-ui/react @radix-ui/styled-components

2. Create Todo Context:
- Create a todoContext.ts file in src folder.
- Define Todo state and context provider:

import { createContext, useContext } from "react";
import { Todo } from "./types";

const TodoContext = createContext(null);

export const TodoContextProvider = ({ children }: { children: React.ReactNode }) => {
const [todos, setTodos] = useState([]);

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

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

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

return {children};
};

export const useTodos = () => useContext(TodoContext);





3. Define Todo List Component:
- Create a TodoList.tsx component in src folder.
- Import necessary hooks and components:

import { useState, useEffect } from "react";
import styled from "styled-components";
import { Todo } from "./types";
import { useTodos } from "./todoContext";




- Define UI and logic:

     const TodoList: React.FC = () => {
       const [searchTerm, setSearchTerm] = useState<string>("");
       const { todos, addTodo, deleteTodo, toggleTodoCompleted } = useTodos();

       useEffect(() => {
         const todosFromLocalStorage = localStorage.getItem("todos") ? JSON.parse(localStorage.getItem("todos")!) : [];
         setTodos(todosFromLocalStorage);
       }, []);

       useEffect(() => {
         localStorage.setItem("todos", JSON.stringify(todos!));
       }, [todos]);

       const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
         setSearchTerm(e.target.value);
       };

       return (
         <Wrapper>
           <Header>
             <h1>Todo List</h1>
             <div>
               <input type="text" value={searchTerm} onChange={handleSearch} placeholder="Search todos..." />
             </div>
           </Header>
           <ul>
             {todos!
               .filter((todo) => todo.title.toLowerCase().includes(searchTerm.toLowerCase()))
               .map((todo) => (
                 <li key={todo.id}>
                   <label>
                     <input
                       type="checkbox"
                       checked={todo.completed}
                       onChange={() => toggleTodoCompleted(todo.id)}
                     />
                     <span>{todo.title}</span>
                   </label>
                   <button onClick={() => deleteTodo(todo.id)}>Delete</button>
                 </li>
               ))}
           </ul>
           <AddTodoForm onSubmit={(e) => {
             e.preventDefault();
             const newTodo = e.target.querySelector("input[name='todo']");
             if (newTodo.value !== "") {
               addTodo(newTodo.value);
               newTodo.value = "";
             }
           }}>
             <input type="text" name="todo" placeholder="Add a new todo..." />
             <button type="submit">Add</button>
           </AddTodoForm>
         </Wrapper>
       );
     };

     export default TodoList;




4. Create Styling Component:
- Create a style.ts file in src folder.
- Define Radix styled components:

import { styled } from "@radix-ui/styled-components";

export const Wrapper = styled("div", {
display: "flex",
flexDirection: "column",
gap: "1rem",
});

export const Header = styled("header", {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "1rem",
borderBottom: "1px solid #ddd",
});

export const AddTodoForm = styled("form", {
display: "flex",
alignItems: "center",
gap: "1rem",
padding: "1rem",
backgroundColor: "#f5f5f5",
borderRadius: "5px",
});

export const TodoList = styled("ul", {
listStyleType: "none",
display: "flex",
flexDirection: "column",
padding: 0,
gap: "1rem",
overflowY: "auto",
});

export const TodoListItem = styled("li", {
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "1rem",
backgroundColor: "#fff",
borderRadius: "5px",
"&:hover": { backgroundColor: "#f5f5f5" },
});

export const TodoLabel = styled("label", {
display: "flex",
alignItems: "center",
gap: "1rem",
});

export const CompletedTodoSpan = styled("span", {
textDecoration: "line-through",
color: "#999",
opacity: 0.5,
});

export const DeleteButton = styled("button", {
padding: "0.25rem 0.5rem",
borderRadius: "5px",
backgroundColor: "#d33",
color: "#fff",
border: "none",
cursor: "pointer",
"&:hover": { backgroundColor: "#c00" },
});





5. Render App:
- In App.tsx, import TodoList and wrap it in TodoContextProvider.
- Return TodoList component as children.

6. Start the App:
- Run yarn start to start the development server.

You'll have a fully functional React + TypeScript todo list app with CRUD operations and local storage persistence using Radix for UI.

Abbos Nurgulshanov
10 February 2024

1. Create a new React app.



npx create-react-app todo-app-ts



2. Install the required dependencies.



yarn add @radix-ui/react-dropdown-menu @radix-ui/react-input @radix-ui/react-popover @radix-ui/react-toggle radix @emotion/react @emotion/styled



3. Create a new file called src/components/TodoItem.tsx.

import { useState } from 'react';
import {
  ArrowLeftIcon,
  CheckIcon,
  CloseIcon,
  StyledMenu,
  StyledMenuItem,
  StyledPopover,
  styled,
} from '@radix-ui/react-dropdown-menu';
import { StyledButton, StyledCheckbox } from '@radix-ui/react-toggle';
import {
  Container,
  Input,
  Task,
  Textbox,
  Title,
} from './styles';

interface TodoItemProps {
  todo: {
    id: string;
    title: string;
    completed: boolean;
  };
  onToggleCompleted: (id: string) => void;
  onEditTodo: (id: string) => void;
  onDeleteTodo: (id: string) => void;
}

const TodoItem: React.FC<TodoItemProps> = ({ todo, onToggleCompleted, onEditTodo, onDeleteTodo }) => {
  const [isEditing, setIsEditing] = useState(false);
  const [title, setTitle] = useState(todo.title);

  const handleToggleCompleted = () => {
    onToggleCompleted(todo.id);
  };

  const handleEditTodo = () => {
    setIsEditing(true);
  };

  const handleDeleteTodo = () => {
    onDeleteTodo(todo.id);
  };

  const handleSaveTodo = () => {
    setIsEditing(false);
    onEditTodo(todo.id, title);
  };

  return (
    <Container>
      <StyledCheckbox checked={todo.completed} onChange={handleToggleCompleted}>
        <CheckIcon />
      </StyledCheckbox>
      {isEditing ? (
        <Textbox>
          <Input value={title} onChange={(e) => setTitle(e.target.value)} />
          <StyledButton onClick={handleSaveTodo}>Save</StyledButton>
        </Textbox>
      ) : (
        <Task completed={todo.completed}>{todo.title}</Task>
      )}
      <StyledPopover>
        <StyledButton asChild>
          <ArrowLeftIcon />
        </StyledButton>
        <StyledMenu>
          <StyledMenuItem onClick={handleEditTodo}>Edit</StyledMenuItem>
          <StyledMenuItem onClick={handleDeleteTodo}>Delete</StyledMenuItem>
        </StyledMenu>
      </StyledPopover>
    </Container>
  );
};

export default TodoItem;




4. Create a new file called src/components/TodoList.tsx.

import { useState } from 'react';
import TodoItem from './TodoItem';

interface Todo {
  id: string;
  title: string;
  completed: boolean;
}

const TodoList: React.FC = () => {
  const [todos, setTodos] = useState<Todo[]>([]);

  const addTodo = (title: string) => {
    const newTodo = {
      id: Date.now().toString(),
      title,
      completed: false,
    };

    setTodos((prevTodos) => [...prevTodos, newTodo]);
  };

  const toggleCompleted = (id: string) => {
    setTodos((prevTodos) => {
      const updatedTodos = prevTodos.map((todo) => {
        if (todo.id === id) {
          return {
            ...todo,
            completed: !todo.completed,
          };
        }

        return todo;
      });

      return updatedTodos;
    });
  };

  const editTodo = (id: string, title: string) => {
    setTodos((prevTodos) => {
      const updatedTodos = prevTodos.map((todo) => {
        if (todo.id === id) {
          return {
            ...todo,
            title,
          };
        }

        return todo;
      });

      return updatedTodos;
    });
  };

  const deleteTodo = (id: string) => {
    setTodos((prevTodos) => prevTodos.filter((todo) => todo.id !== id));
  };

  return (
    <div>
      <Title>Todo List</Title>
      <Input placeholder="Add a new todo..." onKeyPress={(e) => { if (e.key === 'Enter') addTodo(e.target.value); }} />
      {todos.map((todo) => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggleCompleted={toggleCompleted}
          onEditTodo={editTodo}
          onDeleteTodo={deleteTodo}
        />
      ))}
    </div>
  );
};

export default TodoList;


5. Create a new file called src/styles.ts.

import styled from '@emotion/styled';

export const Container = styled.div`
  display: flex;
  align-items: center;
  padding: 12px;
  border-bottom: 1px solid #e0e0e0;
`;

export const Input = styled.input`
  width: 100%;
  padding: 12px;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
`;

export const Task = styled.p`
  flex-grow: 1;
  margin-left: 12px;
  text-decoration: ${(props: { completed: boolean }) => (props.completed ? 'line-through' : 'none')};
`;

export const Textbox = styled.div`
  display: flex;
  align-items: center;
  width: 100%;
  padding: 12px;
  border: 1px solid #e0e0e0;
  border-radius: 4px;
`;

export const Title = styled.h1`
  margin-bottom: 32px;
`;


7. Run the app.

yarn start



8. Open your browser to http://localhost:3000 and you should see your todo list app.

Abbos Nurgulshanov
10 February 2024

1. Create a new React project.


npx create-react-app todo-app


2. Install the necessary dependencies.


npm install @radix-ui/react-listbox @radix-ui/react-checkbox @radix-ui/react-input


3. Create a new file called Todo.tsx in the src folder.

import { useState } from "react";
import { Listbox, ListboxOption } from "@radix-ui/react-listbox";
import { Checkbox } from "@radix-ui/react-checkbox";
import { TextInput } from "@radix-ui/react-input";

const Todo = () => {
  const [todos, setTodos] = useState<string[]>([]);
  const [newTodo, setNewTodo] = useState("");

  const addTodo = () => {
    if (newTodo !== "") {
      setTodos([...todos, newTodo]);
      setNewTodo("");
    }
  };

  const removeTodo = (todo: string) => {
    setTodos(todos.filter((t) => t !== todo));
  };

  return (
    <div>
      <TextInput
        value={newTodo}
        onChange={(e) => setNewTodo(e.target.value)}
        placeholder="Add a new todo"
      />
      <button onClick={addTodo}>Add</button>

      <Listbox>
        {todos.map((todo) => (
          <ListboxOption key={todo} value={todo}>
            {todo}
            <Checkbox onClick={() => removeTodo(todo)} />
          </ListboxOption>
        ))}
      </Listbox>
    </div>
  );
};

export default Todo




4. Create a new file called App.tsx in the src folder.

import Todo from "./Todo";

const App = () => {
  return (
    <div>
      <h1>Todo List</h1>
      <Todo />
    </div>
  );
};

export default App;




5. Add the following code to the index.tsx file.

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(<App />, document.getElementById("root"));




6. Start the development server.


npm start


7. Open your browser and go to http://localhost:3000 to see your todo list app.

1