DEV Community

codetobuild
codetobuild

Posted on

TODO App with Reactjs - Beginners

Project folder structure

Image description

App component

import React from "react";
import Todo from "./components/Todo.component";

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

export default App;
Enter fullscreen mode Exit fullscreen mode

Todo Component

Image description

import { useState } from "react";
import "../styles/todo.css";
import TaskItem from "./TaskItem.component";

let taskId = 1;

const Todo = () => {
  const [inputText, setInputText] = useState("");
  const [tasks, setTasks] = useState([]);

  // function to update tasks state
  const updateTasks = (updatedTasks) => setTasks([...updatedTasks]);

  // function to handle addition of new tasks
  const handleAddTaskButtonClicked = (e) => {
    e.preventDefault();
    if (inputText.trim().length === 0) {
      return; // no need to add new tasks
    }
    taskId += 1;
    const newTask = {
      id: taskId,
      desc: inputText,
      status: {
        complete: false,
      },
    };

    // update state values
    updateTasks([...tasks, newTask]);
    setInputText("");
  };

  // control input element with component state
  const handleInputChange = (e) => {
    e.preventDefault();

    setInputText(() => e.target.value);
  };
  // add new task on enter key clicked
  const handleInputKeyDown = (e) => {
    const keycode = e.keyCode;
    // check is key pressed is enter key
    if (keycode === 13) {
      handleAddTaskButtonClicked(e);
    }
  };

  return (
    <div className="todo_wrapper">
      <h1 className="todo_header">TODO LIST</h1>
      <div className="todo_input_wrapper">
        <input
          className="todo_input_element"
          type="text"
          placeholder="What needs to be done?"
          value={inputText}
          onChange={handleInputChange}
          onKeyDown={handleInputKeyDown}
        />
        <button className="add_btn" onClick={handleAddTaskButtonClicked}>
          ADD
        </button>
      </div>
      <div className="tasks_wrapper">
        {tasks.map((taskItem, index) => (
          <TaskItem
            key={index}
            allTasks={tasks}
            updateTasks={updateTasks}
            taskItem={taskItem}
          />
        ))}
      </div>
    </div>
  );
};

export default Todo;
Enter fullscreen mode Exit fullscreen mode

TaskItem component

Image description

import React from "react";
import "../styles/todo.css";

const TaskItem = ({ updateTasks, allTasks, taskItem }) => {
  // function to handle tasks checkbox clicked
  const handleTaskCheckboxClicked = (e, taskItem) => {
    let updatedTasks = allTasks.map((elem) => {
      if (elem.id === taskItem.id) {
        elem.status.complete = !taskItem.status.complete; // toggle the status
      }
      return elem;
    });
    updateTasks(updatedTasks);
  };

  return (
    <li
      className={`task_item ${
        taskItem.status.complete === true ? "checked" : ""
      }`}
      key={taskItem.id}
    >
      <input
        type="checkbox"
        className="task_checkbox"
        onClick={(e) => handleTaskCheckboxClicked(e, taskItem)}
      />
      <p className="">{taskItem.desc}</p>
    </li>
  );
};

export default TaskItem;

Enter fullscreen mode Exit fullscreen mode

CSS code

.todo_wrapper {
  max-width: 600px;
  padding: 20px;
  background-color: rgb(55, 68, 95);
  margin: auto;
  margin-top: 50px;
  border-radius: 10px;
  box-shadow: 8px 13px 23px 0px rgba(0, 0, 0, 0.2);
}
.todo_header {
  color: white;
  text-align: center;
}
.todo_input_wrapper {
  display: flex;
  align-items: stretch;
}
.todo_input_element {
  flex-grow: 1;
  padding: 10px 10px;
  font-size: 1.1em;
  font-weight: 500;
  outline: none;
  border: none;
  border-radius: 5px 0 0 5px;
}
.add_btn {
  border: none;
  outline: none;
  padding: 0px 20px;
  font-size: 1.1em;
  font-weight: 700;
  background-color: lightgray;
  cursor: pointer;
  border-radius: 0 5px 5px 0;
}
.tasks_wrapper {
  padding: 20px 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.task_item {
  display: flex;
  gap: 10px;
  align-items: center;
  list-style-type: none;
  font-weight: 600;
  border-radius: 5px;
  background-color: lightgreen;
  padding: 10px 15px;
}

.task_item:hover {
  background-color: lime;
}

.task_item p {
  flex-grow: 1;
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.task_checkbox {
  position: relative;
  visibility: none;
  cursor: pointer;
  margin: 0;
}

.task_checkbox::before {
  content: "";
  font-weight: 800;
  width: 1.2em;
  height: 1.2em;
  background-color: white;
  border: 2px solid black;
  position: absolute;
  top: 0;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: translate(-25%, -25%);
}

/* styles for task completed */
.task_item.checked {
  text-decoration: line-through;
  color: darkslategrey;
  background-color: lightslategray;
}
.task_item.checked:hover {
  background-color: lightgrey;
}

.task_item.checked .task_checkbox::before {
  content: "✓";
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)