Level-up your JavaScript and React by building a Todo app (tutorial)
Tags: javascript, react, tutorial, beginners
Published: 2020-07-22 19:20:16

We will build *THE* classic React app: a TodoList. It’s a simple app and yet full of interesting React and JavaScript concepts.

![alt text](https://res.cloudinary.com/dorcersy5/image/upload/v1595968037/finaltest2_fsv0rz.gif)

We will cover:

- Creating functional React components and managing their state with hooks.
- Handling conditional rendering in React.
- Using PropTypes for a safer code.
- **JavaScript concepts:** the ternary operator, array methods, and the spread operator.
- Refactoring code to reduce code redundancy and increase code readability.

You can find the final source code [here](https://github.com/Amirmoh10/TodoListApp.git) and the live version [here](https://romantic-galileo-162862.netlify.app/).

# The app requirements

The app allows us to perform the following operations:

- Add a todo (it gets added to the *Pending* section).
- Complete a todo (it gets added to the *Completed* section).
- Delete a todo (whether it’s pending or completed).

# User Interface Mock

Here’s a simple sketch of the UI of our application:

![https://cdn-images-1.medium.com/max/1600/1*oKILiFhNvhww5fBCBpQQsA.png](https://cdn-images-1.medium.com/max/1600/1*oKILiFhNvhww5fBCBpQQsA.png)


As you can see, our UI consists of the following parts:

- **Text input field** where the user types a todo.
- **Pending section:** contains the todos added by the user that have not been completed yet.
- **Completed section:** contains the todos completed by the user.

# Project Setup

We use [create-react-app](https://github.com/facebook/create-react-app) CLI tool to bootstrap our project:

```
npx create-react-app todo-app
```

This gives us the necessary boilerplate to immediately get productive and not worry about any build tools.

We’re now ready for implementation.

> **Note:** to keep this article short and focused, we will not get into details about the CSS part of the app. To have a similar look to the final result, make sure to copy the same code in App.css file and use the same classNames.

# Adding a heading with the App title

Let’s delete the boilerplate code in *App.js* and add a header that contains the title of our todo app:

```jsx
import React from "react";

import "./App.css";


function App() {
  return (
    <div className="app">
        <h1>Todo</h1>
    </div>
  );
}
export default App;
```

![](https://res.cloudinary.com/dorcersy5/image/upload/v1595963144/title_v8ln9i.gif)


# Adding the Input field

Let's add a basic text input element for the user to type a todo.

To keep track of the value in the input field, we need to save and update that value whenever the user types-in something.

In React we store our application data in the app state. To manage our text input state, we use `React.useState` hook.

> **Note:** You can find a detailed article about handling state with React.useState hook [here](https://dev.to/amirmoh10/cool-kids-handle-state-with-hooks-52p3).

We can import the `useState` function from React as follows:

```jsx
import React, { useState } from "react";
```

The **typed-in todo** by the user has a text value. Therefore, let’s initialize it with an empty string:

```jsx
const [typedInTodo, setTypedInTodo] = useState("");
```

`useState` provides an array that contains 2 elements:

- A `typedInTodo` value that we can use to populate the `input` field.
- A `setTypedInTodo` function to update the todo. We’ll see later how to do that.

```jsx
import React, { useState } from "react";

import "./App.css";


function App() {
  const [typedInTodo, setTypedInTodo] = useState("");

  return (
    <div className="app">
        <h1>Todo</h1>
        <input type="text" placeholder="Add todo..." value={typedInTodo} />
    </div>
  );
}
export default App;
```

![](https://res.cloudinary.com/dorcersy5/image/upload/v1595963200/inputTest_q7j5dd.gif)

# Handling changes to the input

If we try to type anything in the input field, we’ll notice that it stays empty. This is because we‘re not updating its value based on what the user is typing.

To react to the user typing, we add an `onChange` event listener to the input field.

Our event listener receives an `event` parameter that we can use to extract the typed-in value and update the state with it:

```jsx
onChange={(event) => setTypedInTodo(event.target.value)}
```

```jsx
import React, { useState } from "react";

import "./App.css";


function App() {
  const [typedInTodo, setTypedInTodo] = useState("");

  return (
    <div className="app">
      <h1>Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
      />
    </div>
  );
}
export default App;
```

Now if we type something in the input field, it gets updated. We can check if the state is updated by outputting the new value of `typedInTodo` to the console:

```jsx
console.log(typedInTodo)
```

![](https://res.cloudinary.com/dorcersy5/image/upload/v1595963247/inputTestConsole_verccd.gif)

# Let’s make it possible to submit a todo

Since the pending section will hold the submitted todos, it has to have its own state to store such data. We define its state, similarly to how we did it for the typed-in todo, using `React.useState`*.* Since it’s a list, we need an array to store this data. Initially, it is an empty array:

```jsx
const [pendingTodos, setPendingTodos] = useState([]);
```

```jsx
import React, { useState } from "react";

import "./App.css";

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);

  return (
    <div className="app">
      <h1>Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
      />
    </div>
  );
}
export default App;
```

Let’s now make it possible to add a todo to the pending section by hitting the “Enter” key.

We can do so by adding the `onKeyDown` event listener to the input field. The listener function adds the typed-in todo to the pending section if the following conditions hold:

- The user pressed the “Enter” key.
- The typed-in todo is not empty. To remove spaces when checking that, we can use `String.prototype.trim()` method.

To add the todo to the `pendingTodos`, we use the *[Spread operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax), which* allows us to expand the array into individual elements and add a new todo to it.

We should also not forget to clear the input field once the todo is submitted. We can do so by updating the `typedInTodo` to an empty string.

```jsx
import React, { useState } from "react";

import "./App.css";

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);

  function onKeyDown(e) {
    if (e.key === "Enter" && typedInTodo.trim()) {
      setPendingTodos([...pendingTodos, typedInTodo]);
      setTypedInTodo("");
    }
  }

  return (
    <div className="app">
      <h1>Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
        onKeyDown={onKeyDown}
      />
    </div>
  );
}
export default App;
```

Let's see if our logic works properly by adding some logs:

```jsx
console.log(typedInTodo);
console.log(pendingTodos);
```

![](https://res.cloudinary.com/dorcersy5/image/upload/v1595963478/inputTestConsole2_iseyfx.gif)

> **Note:** if your console is logging values twice then you need to remove the StrictMode element from  index.js file.

# Showing the Pending todos

The todos submitted by the user are stored in the state, but we can’t see them in the UI yet.

Let’s add a “Pending” section to show these todos.

The pending section has: 
- **Title:** an 'h2' header named "Pending" that appears dimmed when the section is empty and appears bold whenever a todo is added. 
- **List:** contains the current pending todos. 

We use `Array.prototype.map` method to map each pending todo in `pendingTodos` to a div that has:

- The text of the todo.
- A button to complete the todo.
- A button to delete the todo.

```jsx
import React, { useState } from "react";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";

import "./App.css";

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);

  function onKeyDown(e) {
    if (e.key === "Enter" && typedInTodo.trim()) {
      setPendingTodos([...pendingTodos, typedInTodo]);
      setTypedInTodo("");
    }
  }

  return (
    <div className="app">
      <h1>Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
        onKeyDown={onKeyDown}
      />
      <div className="sectionsContainer">
        <div className="todoContainer">
          <h2
            className={
              pendingTodos.length > 0
                ? "boldSectionTitle"
                : "dimmedSectiontTitle"
            }
          >
            Pending
          </h2>
          <div>
            {pendingTodos.map((todo, index) => (
              <div key={index} className="todoItem">
                <p>{todo}</p>
                <div className="buttonsSection">
                  <button className="transparent completeButton">
                    <CheckOutlined className="icon" />
                  </button>
                  <button className="transparent deleteButton">
                    <CloseOutlined className="icon" />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;

```

![](https://res.cloudinary.com/dorcersy5/image/upload/v1595963760/pendingShowButtons_cc89xy.gif)

> **Note:** your console will inform you that every rendered todo task in our components needs its own key attribute to identify it from its siblings and that is why we added such attribute to the todos.

# Adding the delete ❌ functionality

Let's make the delete button work. Our delete function:

- Gets the index of the todo to delete.
- Filters out the todo from the current list of pending todos, by removing any todo that has the same index as the target one.

> **Note:** the filter function passed to `Array.prototype.filter` accepts 2 arguments: the todo itself (which we don't need and therefore we labeled it as `_` ) and the index of the todo.

```jsx
import React, { useState } from "react";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";

import "./App.css";

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);

  function deleteTodo(todoIndex) {
    const filteredTodos = pendingTodos.filter(
      (_, index) => todoIndex !== index
    );
    setPendingTodos(filteredTodos);
  }

  function onKeyDown(e) {
    if (e.key === "Enter" && typedInTodo.trim()) {
      setPendingTodos([...pendingTodos, typedInTodo]);
      setTypedInTodo("");
    }
  }

  return (
    <div className="app">
      <h1>Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
        onKeyDown={onKeyDown}
      />
      <div className="sectionsContainer">
        <div className="todoContainer">
          <h2
            className={
              pendingTodos.length > 0
                ? "boldSectionTitle"
                : "dimmedSectiontTitle"
            }
          >
            Pending
          </h2>
          <div>
            {pendingTodos.map((todo, index) => (
              <div key={index} className="todoItem">
                <p>{todo}</p>
                <div className="buttonsSection">
                  <button className="transparent completeButton">
                    <CheckOutlined className="icon" />
                  </button>
                  <button
                    className="transparent deleteButton"
                    onClick={() => deleteTodo(index)}
                  >
                    <CloseOutlined className="icon" />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;
```

![](https://res.cloudinary.com/dorcersy5/image/upload/v1595963969/pendingDelete_uojqht.gif)


# Adding the complete ✅ functionality

We first need to create a state value for the completed todos:

```jsx
 const [completedTodos, setCompletedTodos] = useState([]);
```

The complete function:

- Gets the pending todo as an argument.
- Adds the pending todo to `completedTodos` by using the spread operator [...].
- Deletes the todo from the `pendingTodos` array.

```jsx
import React, { useState } from "react";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";

import "./App.css";

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);
  const [completedTodos, setCompletedTodos] = useState([]);

  function completeTodo(todoIndex) {
    const pendingTask = pendingTodos[todoIndex];
    setCompletedTodos([...completedTodos, pendingTask]);
    deleteTodo(todoIndex);
  }

  function deleteTodo(todoIndex) {
    const filteredTodos = pendingTodos.filter(
      (_, index) => todoIndex !== index
    );
    setPendingTodos(filteredTodos);
  }

  function onKeyDown(e) {
    if (e.key === "Enter" && typedInTodo.trim()) {
      setPendingTodos([...pendingTodos, typedInTodo]);
      setTypedInTodo("");
    }
  }

  return (
    <div className="app">
      <h1>Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
        onKeyDown={onKeyDown}
      />
      <div className="sectionsContainer">
        <div className="todoContainer">
          <h2
            className={
              pendingTodos.length > 0
                ? "boldSectionTitle"
                : "dimmedSectiontTitle"
            }
          >
            Pending
          </h2>
          <div>
            {pendingTodos.map((todo, index) => (
              <div key={index} className="todoItem">
                <p>{todo}</p>
                <div className="buttonsSection">
                  <button
                    className="transparent completeButton"
                    onClick={() => completeTodo(index)}
                  >
                    <CheckOutlined className="icon" />
                  </button>
                  <button
                    className="transparent deleteButton"
                    onClick={() => deleteTodo(index)}
                  >
                    <CloseOutlined className="icon" />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;
```

Since the Completed section is exactly the same as the Pending one, let's copy paste the same code and just remove the complete button from it and update the section's heading:

```jsx
import React, { useState } from "react";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";

import "./App.css";

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);
  const [completedTodos, setCompletedTodos] = useState([]);

  function completeTodo(todoIndex) {
    const pendingTask = pendingTodos[todoIndex];
    setCompletedTodos([...completedTodos, pendingTask]);
    deleteTodo(todoIndex, "pending");
  }

  function deleteTodo(todoIndex) {
    const filteredTodos = pendingTodos.filter(
      (_, index) => todoIndex !== index
    );
    setPendingTodos(filteredTodos);
  }

  function onKeyDown(e) {
    if (e.key === "Enter" && typedInTodo.trim()) {
      setPendingTodos([...pendingTodos, typedInTodo]);
      setTypedInTodo("");
    }
  }

  return (
    <div className="app">
      <h1>Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
        onKeyDown={onKeyDown}
      />
      <div className="sectionsContainer">
        <div className="todoContainer">
          <h2
            className={
              pendingTodos.length > 0
                ? "boldSectionTitle"
                : "dimmedSectiontTitle"
            }
          >
            Pending
          </h2>
          <div>
            {pendingTodos.map((todo, index) => (
              <div key={index} className="todoItem">
                <p>{todo}</p>
                <div className="buttonsSection">
                  <button
                    className="transparent completeButton"
                    onClick={() => completeTodo(index)}
                  >
                    <CheckOutlined className="icon" />
                  </button>
                  <button
                    className="transparent deleteButton"
                    onClick={() => deleteTodo(index)}
                  >
                    <CloseOutlined className="icon" />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="todoContainer">
          <h2
            className={
              completedTodos.length > 0
                ? "boldSectionTitle"
                : "dimmedSectiontTitle"
            }
          >
            Completed
          </h2>
          <div>
            {completedTodos.map((todo, index) => (
              <div key={index} className="todoItem">
                <p>{todo}</p>
                <div className="buttonsSection">
                  <button
                    className="transparent deleteButton"
                    onClick={() => deleteTodo(index)}
                  >
                    <CloseOutlined className="icon" />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;

```

![](https://res.cloudinary.com/dorcersy5/image/upload/v1595964255/completedTodos_hcdvra.gif)

Notice that the deleteTodo function only works with pending todos. 

# Making the delete ❌ functionality work in the Completed Section

To make our `deleteTodo` function support both sections, we should provide it a second argument that tells it what the target section is.

Based on that, the `deleteTodo` function can then know whether to delete a pending todo or a completed one. 

After all the only things that change when deleting a completed or a pending todo are the target todo list and its state setter function:

```jsx
import React, { useState } from "react";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";

import "./App.css";

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);
  const [completedTodos, setCompletedTodos] = useState([]);

  function completeTodo(todoIndex) {
    const pendingTask = pendingTodos[todoIndex];
    setCompletedTodos([...completedTodos, pendingTask]);
    deleteTodo(todoIndex, "pending");
  }

  function deleteTodo(todoIndex, targetSection) {
    const targetList =
      targetSection === "pending" ? pendingTodos : completedTodos;
    const setter =
      targetSection === "pending" ? setPendingTodos : setCompletedTodos;
    const filteredTodos = targetList.filter((_, index) => todoIndex !== index);
    setter(filteredTodos);
  }

  function onKeyDown(e) {
    if (e.key === "Enter" && typedInTodo.trim()) {
      setPendingTodos([...pendingTodos, typedInTodo]);
      setTypedInTodo("");
    }
  }

  return (
    <div className="app">
      <h1>Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
        onKeyDown={onKeyDown}
      />
      <div className="sectionsContainer">
        <div className="todoContainer">
          <h2
            className={
              pendingTodos.length > 0
                ? "boldSectionTitle"
                : "dimmedSectiontTitle"
            }
          >
            Pending
          </h2>
          <div>
            {pendingTodos.map((todo, index) => (
              <div key={index} className="todoItem">
                <p>{todo}</p>
                <div className="buttonsSection">
                  <button
                    className="transparent completeButton"
                    onClick={() => deleteTodo(index, "pending")}
                  >
                    <CheckOutlined className="icon" />
                  </button>
                  <button
                    className="transparent deleteButton"
                    onClick={() => deleteTodo(index, "completed")}
                  >
                    <CloseOutlined className="icon" />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
        <div className="todoContainer">
          <h2
            className={
              completedTodos.length > 0
                ? "boldSectionTitle"
                : "dimmedSectiontTitle"
            }
          >
            Completed
          </h2>
          <div>
            {completedTodos.map((todo, index) => (
              <div key={index} className="todoItem">
                <p>{todo}</p>
                <div className="buttonsSection">
                  <button
                    className="transparent deleteButton"
                    onClick={() => deleteTodo(index)}
                  >
                    <CloseOutlined className="icon" />
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

export default App;

```
![](https://res.cloudinary.com/dorcersy5/image/upload/v1595968037/finaltest2_fsv0rz.gif)

The todo app is working now, but we should try to make our code more readable by removing some redundancy.

# Refactoring

If you look at our rendered sections code you can notice that it is just one **list** of todos in a section that we duplicated to create another section. So why don’t we just create that **list** as a **single reusable** component and render our sections lists *[Conditionally](https://reactjs.org/docs/conditional-rendering.html)* based on the necessary props we pass to that component.

What are these **props** that our list component needs in order to render the sections we created in the previous approach?

- **sectionType:** either`"Pending"` or `"Completed"`
- **todoList:** the list of todos to render (either completedTodos or pendingTodos).
- `deleteTodo` and `completeTodo` functions.

Let’s refactor our code following these steps :

- Create a variable named `sectionTypeTitle` to store the values of the first prop and avoid potential typos.
- Create our component, call it `TodoList` and pass to it the props.
- Show the appropriate section header based on `sectionType`.
- Only render the complete button if the `sectionType` is `"pending"`.
- Map through the passed `todoList` and render the todos.

```jsx
import React, { useState } from "react";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";

import "./App.css";

const sectionTypeTitle = {
  completed: "Completed",
  pending: "Pending",
};

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);
  const [completedTodos, setcompletedTodos] = useState([]);

  function completeTodo(todoIndex) {
    const pendingTask = pendingTodos[todoIndex];
    setcompletedTodos([...completedTodos, pendingTask]);
    deleteTodo(todoIndex, "pending");
  }

  function deleteTodo(todoIndex, todoSection) {
    const targetList =
      todoSection === "pending" ? pendingTodos : completedTodos;
    const setter =
      targetList === pendingTodos ? setPendingTodos : setcompletedTodos;
    const filteredTodos = targetList.filter((_, index) => todoIndex !== index);
    setter(filteredTodos);
  }

  function onKeyDown(e) {
    if (e.key === "Enter" && typedInTodo.trim()) {
      setPendingTodos([...pendingTodos, typedInTodo]);
      setTypedInTodo("");
    }
  }

  return (
    <div className="app">
      <h1 className="title">Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
        onKeyDown={onKeyDown}
      />
      <div className="sectionsContainer">
        <TodoList
          sectionTitle="pending"
          completeTodo={completeTodo}
          deleteTodo={deleteTodo}
          todoList={pendingTodos}
        />
        <TodoList
          sectionTitle="completed"
          todoList={completedTodos}
          deleteTodo={deleteTodo}
        />
      </div>
    </div>
  );
}

export default App;

function TodoList({ sectionTitle, completeTodo, deleteTodo, todoList }) {
  return (
    <div className="todoContainer">
      <h2
        className={
          todoList.length > 0 ? "boldSectionTitle" : "dimmedSectiontTitle"
        }
      >
        {sectionTypeTitle[sectionTitle]}
      </h2>
      <div>
        {todoList.map((todo, index) => (
          <div className="todoItem" key={index}>
            <span>{todo}</span>
            <div className="buttonsSection">
              {sectionTitle === "pending" && (
                <button
                  className="transparent completeButton"
                  onClick={() => completeTodo(index)}
                >
                  <CheckOutlined className="icon" />
                </button>
              )}
              <button
                className="transparent deleteButton"
                onClick={() => deleteTodo(index, sectionTitle)}
              >
                <CloseOutlined className="icon" />
              </button>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

```

![](https://res.cloudinary.com/dorcersy5/image/upload/v1595968037/finaltest2_fsv0rz.gif)


### Adding validation to our code

One last thing we can add to our components is props validation using [prop-types](https://reactjs.org/docs/typechecking-with-proptypes.html). 
Our passed in props need to be of certain types and we need to enforce that in our code to avoid catching bugs.

First, we import `PropTypes` from `'prop-types'` at the top of our app code.

```jsx
import PropTypes from 'prop-types';
```

Then use the validators that come with it to validate the data our `TodoList` component receives.

```jsx
TodoList.propTypes = {
  sectionTitle: PropTypes.oneOf(["pending", "completed"]).isRequired,
  completeTodo: PropTypes.func,
  deleteTodo: PropTypes.func.isRequired,
  todoList: PropTypes.arrayOf(PropTypes.string),
};
```

Let’s add it to our final app code :

```jsx
import React, { useState } from "react";
import PropTypes from "prop-types";
import { CloseOutlined, CheckOutlined } from "@ant-design/icons";

import "./App.css";

const sectionTypeTitle = {
  completed: "Completed",
  pending: "Pending",
};

function App() {
  const [typedInTodo, setTypedInTodo] = useState("");
  const [pendingTodos, setPendingTodos] = useState([]);
  const [completedTodos, setcompletedTodos] = useState([]);

  function completeTodo(todoIndex) {
    const pendingTask = pendingTodos[todoIndex];
    setcompletedTodos([...completedTodos, pendingTask]);
    deleteTodo(todoIndex, "pending");
  }

  function deleteTodo(todoIndex, todoSection) {
    const targetList =
      todoSection === "pending" ? pendingTodos : completedTodos;
    const setter =
      targetList === pendingTodos ? setPendingTodos : setcompletedTodos;
    const filteredTodos = targetList.filter((_, index) => todoIndex !== index);
    setter(filteredTodos);
  }

  function onKeyDown(e) {
    if (e.key === "Enter" && typedInTodo.trim()) {
      setPendingTodos([...pendingTodos, typedInTodo]);
      setTypedInTodo("");
    }
  }

  return (
    <div className="app">
      <h1 className="title">Todo</h1>
      <input
        type="text"
        placeholder="Add todo..."
        value={typedInTodo}
        onChange={(event) => setTypedInTodo(event.target.value)}
        onKeyDown={onKeyDown}
      />
      <div className="sectionsContainer">
        <TodoList
          sectionTitle="pending"
          completeTodo={completeTodo}
          deleteTodo={deleteTodo}
          todoList={pendingTodos}
        />
        <TodoList
          sectionTitle="completed"
          todoList={completedTodos}
          deleteTodo={deleteTodo}
        />
      </div>
    </div>
  );
}

export default App;

function TodoList({ sectionTitle, completeTodo, deleteTodo, todoList }) {
  return (
    <div className="todoContainer">
      <h2
        className={
          todoList.length > 0 ? "boldSectionTitle" : "dimmedSectiontTitle"
        }
      >
        {sectionTypeTitle[sectionTitle]}
      </h2>
      <div>
        {todoList.map((todo, index) => (
          <div className="todoItem" key={index}>
            <span>{todo}</span>
            <div className="buttonsSection">
              {sectionTitle === "pending" && (
                <button
                  className="transparent completeButton"
                  onClick={() => completeTodo(index)}
                >
                  <CheckOutlined className="icon" />
                </button>
              )}
              <button
                className="transparent deleteButton"
                onClick={() => deleteTodo(index, sectionTitle)}
              >
                <CloseOutlined className="icon" />
              </button>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

TodoList.propTypes = {
  sectionTitle: PropTypes.oneOf(["pending", "completed"]).isRequired,
  completeTodo: PropTypes.func,
  deleteTodo: PropTypes.func.isRequired,
  todoList: PropTypes.arrayOf(PropTypes.string),
};
```

# Conclusion

I publish articles monthly and I'm currently looking for my first Frontend Developer job in either Europe or Canada.

Stay tuned by following me on Twitter ([@amir_ghezala](https://twitter.com/amir_ghezala)) or checking [my portfolio](https://www.amirghezala.com/).