Todo using useReducer in React
Tags: react, usereducer
Published: 2021-03-02 21:22:54

## What is React?
React is a `javaScript` library that use to build our user interface for applications. 
### What is `useReducer`
`useReducer` is a react hook that is used to implement our complex state of applications. 
> In this simple todo app I have used react with typescript template
#### Step 1:
First of all, create a project called todo using `create-react-app`
`npx create-react-app todo --template typescript`
After creating the project `cd` to todo folder then run 
`npm start` or `yarn start`

#### Step 2: 
1. Create two folder called **containers** , **components**
2. Create a file called **Todos.tsx** in containers
3. Import Todos.tsx file in App.tsx and use it
```js
import * as React from "react";
import Todos from "./container/Todos";

const App: React.FC = () => {
  return <Todos />;
};
export default App;

```
5. Create an interface that will represent the blueprint of a todo item in Todos.tsx file

```js
export interface TodosProps {
  id: string;
  todoName: string;
  isComplete: boolean;
}

```
6. Create  interfaces for various activities of the app like add todo item, remove todo item and toggle the todo to check that todo item is completed or not
```js
interface AddTodoAction {
  type: "ADD_TODO";
  payload: { name: string };
}

interface ModifyTodoAction {
  type: "TOGGLE_TODO" | "DELETE_TODO";
  payload: { id: string };
}
```
7. action type

```js
export type TodoAction = AddTodoAction | ModifyTodoAction
```
8. Create a reducer function called todoReducer that is used with useReducer to control the state with action. 

```js
const todoReducer = (todos: Array<TodosProps>, action: TodoAction) => {
  switch (action.type) {
    case "ADD_TODO":
      return [...todos, newTodo(action.payload.name)];
    case "TOGGLE_TODO":
      return todos.map((todo) => {
        if (todo.id === action.payload.id) {
          return { ...todo, isComplete: !todo.isComplete };
        }
        return todo;
      });
    case "DELETE_TODO":
      return todos.filter((todo) => todo.id !== action.payload.id);
    default:
      return todos;
  }
};
```
7. Create a new todo item structure with this function
```js
const newTodo = (todoName: string): TodosProps => {
  return { id: uuidv4(), todoName: todoName, isComplete: false };
};
```
8. Todos.tsx 
```js
import * as React from "react";
import { useReducer } from "react";
import { v4 as uuidv4 } from "uuid";
import Todo from "../components/Todo";
import TodoInput from "../components/TodoInput";

export interface TodosProps {
  id: string;
  todoName: string;
  isComplete: boolean;
}

interface AddTodoAction {
  type: "ADD_TODO";
  payload: { name: string };
}

interface ModifyTodoAction {
  type: "TOGGLE_TODO" | "DELETE_TODO";
  payload: { id: string };
}

export type TodoAction = AddTodoAction | ModifyTodoAction;

const todoReducer = (todos: Array<TodosProps>, action: TodoAction) => {
  switch (action.type) {
    case "ADD_TODO":
      return [...todos, newTodo(action.payload.name)];
    case "TOGGLE_TODO":
      return todos.map((todo) => {
        if (todo.id === action.payload.id) {
          return { ...todo, isComplete: !todo.isComplete };
        }
        return todo;
      });
    case "DELETE_TODO":
      return todos.filter((todo) => todo.id !== action.payload.id);
    default:
      return todos;
  }
};

const newTodo = (todoName: string): TodosProps => {
  return { id: uuidv4(), todoName: todoName, isComplete: false };
};

const Todos: React.FC = () => {
  const [todos, dispatch] = useReducer(todoReducer, []);

  const renderTodos = todos.map((todo) => (
    <Todo
      key={todo.id}
      id={todo.id}
      todoName={todo.todoName}
      isComplete={todo.isComplete}
      dispatch={dispatch}
    />
  ));

  console.log(todos);

  return (
    <div>
      <TodoInput dispatch={dispatch} />
      {renderTodos}
    </div>
  );
};

export default Todos;

```
4. Create a file called **TodoInput.tsx** in components folder

#### TodoInput.tsx
This component is responsible for rendering a form with an input field and a submit button

```js
import * as React from "react";
import { useState } from "react";
import { TodoAction } from "../container/Todos";

interface TodoInputProps {
  dispatch: React.Dispatch<TodoAction>;
}

const TodoInput: React.FC<TodoInputProps> = ({ dispatch }) => {
  const [todoName, setTodoName] = useState("");

  const handleChange = (evt: React.FormEvent<HTMLInputElement>) => {
    setTodoName(evt.currentTarget.value);
  };
  const handleSubmit = (evt: React.FormEvent) => {
    evt.preventDefault();
    dispatch({ type: "ADD_TODO", payload: { name: todoName } });
    setTodoName("");
  };
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Type your todo...."
        value={todoName}
        onChange={handleChange}
      />
      <button type="submit">Add Todo</button>
    </form>
  );
};

export default TodoInput;

```

5. Display all todo item in Todo.tsx file in components folder
```js
import * as React from "react";
import { TodosProps, TodoAction } from "../container/Todos";

export interface Props extends TodosProps {
  dispatch: React.Dispatch<TodoAction>;
}

const Todo: React.FC<Props> = ({ dispatch, id, isComplete, todoName }) => {
  const handleDelete = (id: string) => {
    dispatch({
      type: "DELETE_TODO",
      payload: { id: id },
    });
  };
  const handleToggle = (id: string) => {
    dispatch({
      type: "TOGGLE_TODO",
      payload: { id: id },
    });
  };
  return (
    <div>
      <div>
        <p style={{ textDecoration: `${isComplete ? "line-through" : ""}` }}>
          {todoName}
        </p>
      </div>
      <div>
        <button onClick={() => handleToggle(id)}>Toggle</button>
        <button onClick={() => handleDelete(id)}>Delete</button>
      </div>
    </div>
  );
};

export default Todo;

```