Владислав Калачев

Пишем список задач на React Hooks

204

Доброго времени суток, друзья. В данной статье мы напишем свой список задач, используя React хуки. Данная статья будет интересна в первую очередь начинающим Frontend разработчикам и всем тем, кто хочет просто написать свое первое приложение на React. Для создания первоначального шаблона для разработки порекомендую вам использовать create-react-app либо react-cli-ui. Давайте приступим к самой разработке.

Видео тут:

Структура Todo

Для начала подготовим структуру нашего приложения, оно будет состоять из следующих файлов.- App.js

— ToDo.js

— ToDoForm.js

— index.css

— index.js

В index.js находится инициализация приложения путем добавления App компонента в div с айди #root. В самом App.js будет существовать наш список с элементами (Todo.js) с формой для ввода новых данных (ToDOForm.js) и файлом index.css, где будут храниться все стили приложения.

Давайте опишем структуру приложения в App.js

Пример (App.js):


import React, { useState } from "react";
//components
import ToDo from "./ToDo";
import ToDoForm from "./ToDoForm";

function App() {
  const [todos, setTodos] = useState([]);

  const addTask = (userInput) => {
  };

  const removeTask = (id) => {
  };

  const handleToggle = (id) => {
  };

  return (
    <div className="App">
      <header>
        <h1>Список задач: {todos.length}</h1>
      </header>
      <ToDoForm addTask={addTask} />
      {todos.map((todo) => {
        return (
          <ToDo
            todo={todo}
            key={todo.id}
            toggleTask={handleToggle}
            removeTask={removeTask}
          />
        );
      })}
    </div>
  );
}

export default App;

Компонент хранит в себе состояние задач в виде массива в useState. Имеются три функции для взаимодействия с интерфейсом. 

addTask добавление новой задачи

removeTask удаление задачи

handleToggle переключение задачи с активной на выполненную

Сам компонент возвращает div, в котором имеется заголовок. Для большей наглядности в нем выводится количество элементов текущего списка.

Ниже находится компонент формы (ToDoForm), в которую передаётся функция addTask. И под ним выводится список текущих задач, который благодаря функции map выводит содержимое массива todos, элементами которого является компонент ToDo. В этот компонент передаётся текущий элемент массива todo, указывается key (по базовым правилам работы виртуального дома в React) и функции handleToggle и removeTask, которые были описаны выше. Давайте теперь подробно рассмотрим компонент формы (ToDoForm), в котором будет происходить все основное взаимодействие интерфейса с пользователем.

Форма добавления новой задачи

Пример:


import React, { useState } from "react";

const ToDoForm = ({ addTask }) => {
  const [userInput, setUserInput] = useState("");

  const handleChange = (e) => {
    setUserInput(e.currentTarget.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    addTask(userInput);
    setUserInput("");
  };

  const handleKeyPress = (e) => {
    if (e.key === "Enter") {
      handleSubmit(e);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={userInput}
        type="text"
        onChange={handleChange}
        onKeyDown={handleKeyPress}
        placeholder="Введите значение..."
      />
      <button>Сохранить</button>
    </form>
  );
};

export default ToDoForm;

Посмотрим на компонент ToDoForm. Он состоит из состояния input, которое хранится в отельном useState. Функция handleChange для изменения значений инпута, handleSubmit для отправления значений в список задач и handleKeyPress для работы с кнопкой Enter, по клику на которую будет происходить вызов функции handleSubmit.

Сам компонент это просто форма с одним полем ввода и кнопкой «Сохранить».

Стоит обратить внимание на функцию handleSubmit, внутри которой мы передаем значение из поля ввода в функцию addTask, после чего вызываем функцию setUserInput с пустой строкой. Это нужно, чтобы поле ввода стало пустым сразу после отправления формы.

Элемент списка Todo

Последний из компонентов, который мы рассмотрим, является элемент списка задач.

Пример:


import React from "react";

const ToDo = ({ todo, toggleTask, removeTask }) => {
  return (
    <div key={todo.id + todo.key} className="item-todo">
      <div
        onClick={() => toggleTask(todo.id)}
        className={todo.complete ? "item-text strike" : "item-text"}
      >
        {todo.task}
      </div>
      <div className="item-delete" onClick={() => removeTask(todo.id)}>
        x
      </div>
    </div>
  );
};

export default ToDo;

Как видите это просто div, внутри которого находятся еще два дива. В первом выводится значение самой задачи, а во втором находится крестик для ее удаления. По клику на название задачи происходит вызов функции toggleTask, в которую передается id задачи и в объекте поле complete меняет свое булевое значение (true/false) на противоположное, после чего меняются классы, и запись визуально становится неактивной (зачеркнута линией).

Давайте вернемся в App.js и посмотрим на реализацию функций, которые выполняют всю работу.

Дописываем функции в App

Пример:


import React, { useState } from "react";
//components
import ToDo from "./ToDo";
import ToDoForm from "./ToDoForm";

function App() {
  const [todos, setTodos] = useState([]);

  const addTask = (userInput) => {
    if (userInput) {
      const newItem = {
        id: Math.random().toString(36).substr(2, 9),
        task: userInput,
        complete: false
      };
      setTodos([...todos, newItem]);
    }
  };

  const removeTask = (id) => {
    setTodos([...todos.filter((todo) => todo.id !== id)]);
  };

  const handleToggle = (id) => {
    setTodos([
      ...todos.map((task) =>
        task.id === id ? { ...task, complete: !task.complete } : { ...task }
      )
    ]);
  };

  return (
    <div className="App">
      <header>
        <h1>Список задач: {todos.length}</h1>
      </header>
      <ToDoForm addTask={addTask} />
      {todos.map((todo) => {
        return (
          <ToDo
            todo={todo}
            key={todo.id}
            toggleTask={handleToggle}
            removeTask={removeTask}
          />
        );
      })}
    </div>
  );
}

export default App;

Как видите, в функции addTask используется спред оператор, в результате чего происходит добавление нового элемента с уникальным id, полем task и complete.

Функция removeTask принимает id, по которому фильтруется весь список задач и возвращается новое состояние списка в функцию setTodos.

В функции handleToggle так же принимается id задачи, по которому идет сравнение в массиве и изменение поля complete, если текущая выбранная id равна id из списка.

Немного стилей

И в завершение, чтобы наш список выглядел более привлекательнем, добавим немного CSS.

Пример:


.App {
  text-align: center;
}

.App .item-todo {
  padding: 14px;
  width: 250px;
  margin: 0 auto;
  text-align: left;
  margin-top: 15px;
  background: linear-gradient(
    90deg,
    rgba(93, 12, 255, 1) 0%,
    rgba(155, 0, 250, 1) 100%
  );
  color: #fff;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

.App .item-todo .item-text {
  display: inline-block;
  cursor: pointer;
}

.App input {
  padding: 14px 32px 14px 16px;
  border-radius: 4px 0 0 4px;
  border: 2px solid #5d0cff;
  outline: none;
  background: transparent;
}

.App button {
  padding: 16px 7px;
  border: none;
  border-radius: 0 4px 4px 0;
  cursor: pointer;
  outline: none;
  background: linear-gradient(
    90deg,
    rgba(93, 12, 255, 1) 0%,
    rgba(155, 0, 250, 1) 100%
  );
  color: #fff;
  text-transform: capitalize;
}

.App .item-todo .item-delete {
  display: inline-block;
  cursor: pointer;
  float: right;
  font-size: 22px;
  position: relative;
  left: -4px;
  bottom: 5px;
}

.strike {
  text-decoration: line-through;
}

Заключение

Сегодня мы написали свой список задач, используя современный подход в разработке React Hooks. Мы разобрали базовые кейсы взаимодействия пользователя с таким интерфейсом. Надеюсь, что данный материал был вам полезен. Учитесь, думайте, пишите код. Удачного кодинга, друзья!

Подписывайтесь на наш канал в Telegram и на YouTube для получения самой последней и актуальной информации. 

Комментарии:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *