# Todo List App With React

A few weeks ago I started learning ReactJs. I first watched a crash course to get a brief understanding of what it is all about.

Then I went to the official [docs](https://react.dev/learn) to start fiddling with it. After reading a fair bit of it, I decided to start building small projects and this is one of them.

Let's get started.

## The features

* Alerts the user to enter a task if an empty task is submitted
    
* Clicking the checkbox against the task marks it as done by crossing a grey line through
    
* The **clear completed** button clears all tasks that are marked as done
    
* The **clear all tasks** button clears all tasks in the list whether completed or not
    

Here is the [demo](https://tada-list.netlify.app/) of the app. And here is the project on [github](https://github.com/SUBiango/todo-list-react).

The complete [CSS file.](https://github.com/SUBiango/todo-list-react/blob/main/src/Style.css)

### Import Statements

```javascript
import { useState } from 'react';
import './Style.css';
```

In React, you can either build “top-down” by starting with building the components higher up in the hierarchy or “bottom-up” by working from components lower down.

So I started writing the components using the bottom-up approach.

### The app component

```javascript
function ToDoApp() {
  return (
    <ToDoContainer />
  );
}

export default ToDoApp;
```

Here, I created the ToDoApp function that returns the component that contains all the components of the entire app, **ToDoContainer**.

### The Todo Container

```javascript
function ToDoContainer() {
   // the rest of the code 
}
```

I put all the states and the functions for the entire app in this component and then I passed them down through props to the child components. But more on that later.

For now, let's write the entire UI (user interface) first as it requires just a little effort of thinking before adding interactivity.

### The rest of the components

The `Header` component.

```javascript
function Header() {
  return (
    <h1 className='header'>Ta-Da List App</h1>
  )
}
```

The `AddTask` component.

```javascript
function AddTask() {
    return (
    <form className='add-task'>
      <input  type='text'/>
      <button className='add-task-button'>Add Task</button>
    </form>
  )
}
```

Here we are returning a simple form with an input box and a button to add a task to the list.

The `TaskList` component

```javascript
function TaskList() {
    return (
        <ul className='task-list-ul'>
            <li className='task-list-li'></li>
        </ul>
    )
}
```

The `TodoControls` component returns the **clear completed** and **clear all task** buttons.

```javascript
function TodoControls() {
  return (
    <div className='tada-wrapper'>
      <button className='btn'>Clear all task</button>
      <button className='btn'>Clear completed</button>
    </div>
  )
}
```

The UI is done.

As I mentioned earlier, I used the bottom-up approach to write the UI. That means the first component (`ToDoContainer`) we created at the top is in fact at the very bottom of the app and the last one is at the top.

I then added all of the above components in the `ToDoContainer` to display the static app.

```javascript
function ToDoContainer() {

return (
    <div className='container'>
      <Header />
      <AddTask />
      <TaskList />
      <TodoControls />
    </div>
  )
}
```

This is the result so far.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707738475988/0d0ed87d-5bed-4788-b402-beca2db4b0c5.png align="center")

## Add interactivity

I created three state variables for the **tasks**, the **input value** and the **checkbox** in the `ToDoContainer`. These are the three pieces of the app that change based on user interaction.

Thereafter I added

```javascript
function ToDoContainer() {
  const [tasks, setTasks] = useState([])
  const [inputValue, setInputValue] = useState('')
  const [checkedTask, setCheckedTask] = useState([])
    // the rest of the code
}
```

### Let us wire the handler functions

I first created the `handleChange` function that will trigger when clicked in the input.

```javascript
function handleChange(e) {
    setInputValue(e.target.value)
}
```

Let's create the handler function that will add the input value to the task list.

```javascript
function handleSubmit(e) {
    e.preventDefault()

    if (inputValue.trim() !== '') {
      setTasks([...tasks, {text: inputValue, completed: false}])
      setInputValue('')
    } else {
      alert('Please enter task')
    }
}
```

This `handleSubmit` function ensures that a new task is added to the To-Do list only if the user has entered a non-empty task. If the input is empty, it displays an alert prompting the user to enter a task.

Let's create the `handleToggleTask` function that is responsible for toggling the completion status of a task.

```javascript
 function handleToggleTask(index) {
    const updatedTasks = tasks.map((task, i) => 
      i === index ? {...task, completed: !task.completed} : task
    )
    setTasks(updatedTasks)

    if (checkedTask.includes(index)) {
      setCheckedTask(checkedTask.filter((i) => i !== index))
    } else {
      setCheckedTask(checkedTask.concat(index))
    }
  }
```

This function toggles the completion status of a task, updates the tasks array with the modified task(s), and updates the `checkedTask` array to keep track of completed tasks.

```javascript
 function handleClearCompletedTask() {
    let completedTask = tasks.filter((_, index) => !checkedTask.includes(index))
    setTasks(completedTask)
    setCheckedTask([])
  }

  function handleClearAllTask() {
    tasks.splice(0, tasks.length)
    setTasks([])
  }
```

The `handleClearCompletedTask` removes completed tasks by filtering the tasks array based on the indices stored in `checkedTask`.

And the `handleClearAllTask` removes or clears all the tasks from the to-do list.

### Pass the functions as props

Let us go back to the `AddTask` component and add the state and the handle functions as props.

```javascript
function AddTask(props) {

  const {inputValue, handleChange, handleSubmit} = props

  return (
    <form className='add-task'>
      <input
        type='text'
        value={inputValue}
        onChange={handleChange}
        placeholder='Enter task...'
        className='add-task-input'
      />
      <button 
        onClick={handleSubmit}
        className='add-task-button'>Add Task
      </button>
    </form>
  )
}
```

We are using **"destructuring assignment"** to destructure the `inputValue`, `handleChange` and `handleSubmit` properties from the props object.

Now, let's go back to our task list and fill out what was missing for the functionality.

```javascript
function TaskList({tasks, onToggleTask}) {
  return (
    <ul className='task-list-ul'>
      {tasks.map((task, index) => (
        <li key={index} className='task-list-li'>
          <input
              type='checkbox'
              id={`task-checkbox-${index}`}
              className='task-checkbox'
              checked={task.completed}
              onChange={() => onToggleTask(index)}
            /> 
          <label 
              htmlFor={`task-checkbox-${index}`} 
              className='task-checkbox-label'>
           {' '} {task.text}
          </label>
        </li>
      ))}
    </ul>
  )
}
```

Now our `TaskList` component renders the list of tasks. It maps over the `tasks` array, creating a list item for each task with a checkbox input and a corresponding label. The checkbox's state is determined by the `completed` property of each task. The `onToggleTask` function is called when a checkbox is clicked, passing the index of the task.

```javascript
function TodoControls({onClearCompletedTask, onClearAllTask}) {
  return (
    <div className='tada-wrapper'>
      <button 
        className='btn' 
        onClick={onClearAllTask}
        >Clear all task
        </button>
      <button 
        className='btn' 
        onClick={onClearCompletedTask}>
      Clear completed
      </button>
    </div>
  )
}
```

In this component, we are receiving the functions to clear tasks as props.

## The complete code

```javascript
import { useState } from 'react';
import './Style.css';

function TodoControls({onClearCompletedTask, onClearAllTask}) {
  return (
    <div className='tada-wrapper'>
      <button 
        className='btn' 
        onClick={onClearAllTask}>Clear all task</button>
      <button 
        className='btn' 
        onClick={onClearCompletedTask}>
      Clear completed
      </button>
    </div>
  )
}

function TaskList({tasks, onToggleTask}) {
  return (
    <ul className='task-list-ul'>
      {tasks.map((task, index) => (
        <li key={index} className='task-list-li'>
          <input
              type='checkbox'
              id={`task-checkbox-${index}`}
              className='task-checkbox'
              checked={task.completed}
              onChange={() => onToggleTask(index)}
            /> 
          <label htmlFor={`task-checkbox-${index}`} 
            className='task-checkbox-label'>
           {' '} {task.text}
          </label>
        </li>
      ))}
    </ul>
  )
}

function AddTask(props) {

  const {inputValue, handleChange, handleSubmit} = props

  return (
    <form className='add-task'>
      <input
        type='text'
        value={inputValue}
        onChange={handleChange}
        placeholder='Enter task...'
        className='add-task-input'
      />
      <button 
        onClick={handleSubmit}
        className='add-task-button'>Add Task
      </button>
    </form>
  )
}

function Header() {
  return (
    <h1 className='header'>Ta-Da List App</h1>
  )
}


function ToDoContainer() {

  const [tasks, setTasks] = useState([])
  const [inputValue, setInputValue] = useState('')
  const [checkedTask, setCheckedTask] = useState([])

  function handleChange(e) {
    setInputValue(e.target.value)
  }

  function handleSubmit(e) {
    e.preventDefault()

    if (inputValue.trim() !== '') {
      setTasks([...tasks, {text: inputValue, completed: false}])
      setInputValue('')
    } else {
      alert('Please enter task')
    }
  }

  function handleToggleTask(index) {
    const updatedTasks = tasks.map((task, i) => 
      i === index ? {...task, completed: !task.completed} : task
    )
    setTasks(updatedTasks)

    if (checkedTask.includes(index)) {
      setCheckedTask(checkedTask.filter((i) => i !== index))
    } else {
      setCheckedTask(checkedTask.concat(index))
    }
  }

  function handleClearCompletedTask() {
    let completedTask = tasks.filter((_, index) => !checkedTask.includes(index))
    setTasks(completedTask)
    setCheckedTask([])
  }

  function handleClearAllTask() {
    tasks.splice(0, tasks.length)
    setTasks([])
  }


  return (
    <div className='container'>
      <Header />
      <AddTask 
        inputValue={inputValue}
        handleChange={handleChange}
        handleSubmit={handleSubmit}
      />
      <TaskList
        tasks={tasks}
        onToggleTask={handleToggleTask}
      />
      <TodoControls
        onClearCompletedTask={handleClearCompletedTask}
        onClearAllTask={handleClearAllTask}
      />
    </div>
  )
}

function ToDoApp() {
  return (
    <ToDoContainer />
  );
}

export default ToDoApp;
```

We are finally done.

I may have not done a good job with this but I will keep it going to get better at it.

Thanks for reading.
