Учебный курс по React, часть 18: шестой этап работы над TODO-приложением

Автор оригинала: Bob Ziroll
  • Перевод
  • Tutorial
В сегодняшней части перевода учебного курса по React вам предлагается продолжить работу над Todo-приложением и сделать так, чтобы щелчки по флажкам воздействовали бы на состояние компонента.

image

Часть 1: обзор курса, причины популярности React, ReactDOM и JSX
Часть 2: функциональные компоненты
Часть 3: файлы компонентов, структура проектов
Часть 4: родительские и дочерние компоненты
Часть 5: начало работы над TODO-приложением, основы стилизации
Часть 6: о некоторых особенностях курса, JSX и JavaScript
Часть 7: встроенные стили
Часть 8: продолжение работы над TODO-приложением, знакомство со свойствами компонентов
Часть 9: свойства компонентов
Часть 10: практикум по работе со свойствами компонентов и стилизации
Часть 11: динамическое формирование разметки и метод массивов map
Часть 12: практикум, третий этап работы над TODO-приложением
Часть 13: компоненты, основанные на классах
Часть 14: практикум по компонентам, основанным на классах, состояние компонентов
Часть 15: практикумы по работе с состоянием компонентов
Часть 16: четвёртый этап работы над TODO-приложением, обработка событий
Часть 17: пятый этап работы над TODO-приложением, модификация состояния компонентов
Часть 18: шестой этап работы над TODO-приложением
Часть 19: методы жизненного цикла компонентов
Часть 20: первое занятие по условному рендерингу
Часть 21: второе занятие и практикум по условному рендерингу
Часть 22: седьмой этап работы над TODO-приложением, загрузка данных из внешних источников
Часть 23: первое занятие по работе с формами
Часть 24: второе занятие по работе с формами

Занятие 33. Практикум. TODO-приложение. Этап №6


Оригинал

▍Задание


На этом практическом занятии мы продолжим работу над Todo-приложением, сделаем так, чтобы действия пользователя влияли бы на состояние компонента. Речь идёт о том, чтобы у нас появилась возможность отмечать пункты списка дел как выполненные или невыполненные. Ниже приведён код компонента App, некоторые заготовки и комментарии, имеющиеся в котором, призваны помочь вам в выполнении задания. Собственно говоря, вот что вам предлагается сегодня сделать:

  1. Создайте в компоненте App обработчик события, который реагирует на изменения флажков (речь идёт о событии onChange) и соответствующим образом меняет состояние приложения. Пожалуй, это — самая сложная часть сегодняшнего задания. Для того чтобы с ней справиться — обратите внимание на комментарии и заготовки, представленные в коде.
  2. Передайте соответствующий метод компоненту TodoItem.
  3. В компоненте TodoItem создайте механизм, который, при возникновении события onChange, вызывает переданный экземпляру компонента метод и передаёт ему идентификатор (id) дела, которому соответствует флажок, по которому щёлкнул пользователь.

Вот код компонента App:

import React from "react"
import TodoItem from "./TodoItem"
import todosData from "./todosData"

class App extends React.Component {
    constructor() {
        super()
        this.state = {
            todos: todosData
        }
        this.handleChange = this.handleChange.bind(this)
    }
    
    handleChange(id) {
        // Обновите состояние так, чтобы у элемента с заданным id свойство 
        // completed поменялось бы c false на true (или наоборот).
        // Помните о том, что предыдущую версию состоянию менять не следует.
        // Вместо этого нужно вернуть новую версию состояния, содержащую изменения.
        // (Подумайте о том, как для этого использовать метод массивов map.)
    }
    
    render() {
        const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item}/>)
        
        return (
            <div className="todo-list">
                {todoItems}
            </div>
        )    
    }
}

export default App

▍Решение


Для начала создадим простой механизм проверки вызова метода handleChange(). А именно — приведём его к такому виду:

handleChange(id) {
    console.log("Changed", id)
}

Теперь мы реализуем то, что нужно сделать в соответствии с пунктами 2 и 3 задания. То есть — создадим связь между щелчком по флажку и вызовом метода handleChange() с передачей ему id этого флажка.

Для того чтобы передать экземпляру компонента TodoItem ссылку на handleChange(), мы можем поступить так же, как поступали передавая ему свойства и переписать код создания списка компонентов так:

const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item} handleChange={this.handleChange}/>)

Обратите внимание на то, что свойство handleChange, которое будет доступно компонентам TodoItem, содержит ссылку на метод handleChange экземпляра компонента App. В компоненте TodoItem к этому методу можно обратиться так же, как и к другим передаваемым ему свойствам. На данном этапе работы код TodoItem выглядит так:

import React from "react"

function TodoItem(props) {
    return (
        <div className="todo-item">
            <input 
                type="checkbox" 
                checked={props.item.completed} 
                onChange={() => console.log("Changed!")}
            />
            <p>{props.item.text}</p>
        </div>
    )
}

export default TodoItem

Для вызова метода handleChange в коде компонента можно воспользоваться конструкцией вида props.handleChange(). При этом данному методу нужно передать id элемента. Обработчик события onChange принимает объект события. Нам, для вызова метода handleChange(), этот объект не требуется. Перепишем код, в котором мы назначаем элементу обработчик события onChange, так:

onChange={(event) => props.handleChange(props.item.id)}

Здесь мы вызываем метод handleChange(), передавая ему идентификатор элемента, взятый из переданных ему свойств, из другой функции, которая принимает объект события. Так как мы этот объект здесь не используем, код можно переписать так:

onChange={() => props.handleChange(props.item.id)}

Теперь попробуем запустить приложение и, открыв консоль, пощёлкать по флажкам.


Проверка метода handleChange()

В консоль попадают сообщения, содержащие идентификаторы флажков, по которым мы щёлкаем. Но флажки пока не меняют внешний вид, так как в методе handleChange() ещё не реализован механизм изменения состояния компонента. В результате мы только что справились со второй и третьей частями задания и теперь можем приступить к работе над его первой частью, пожалуй, самой интересной из всех, касающейся работы с состоянием.

Для начала нам нужно решить вопрос, касающийся того, что в состоянии хранится массив, некий элемент которого, в ответ на щелчок по флажку, должен претерпеть изменения, но мы при этом не должны модифицировать массив, хранящийся в старой версии состояния. То есть, например, нельзя просто пройтись по уже имеющемуся в состоянии массиву объектов, найти нужный элемент и изменить его свойство completed. Нам нужно, чтобы, после изменения состояния, был бы сформирован новый массив, один из элементов которого будет изменён, а остальные останутся такими же, какими были раньше. Одним из подходов к формированию такого массива является использование метода массивов map(), упомянутого в комментариях к заданию. Код мы будем писать в методе setState(). Приведём код метода handleChange() из компонента App к следующему виду:

handleChange(id) {
    this.setState(prevState => {
        
    })
}

Теперь, с помощью метода map(), пройдёмся по массиву prevState.todos и поищем в нём нужный нам элемент, то есть тот, id которого передано методу handleChange(), после чего изменим его свойство completed. Метод map() возвращает новый массив, который и будет использоваться в новом состоянии приложения, поэтому мы запишем этот массив в константу. Вот как это выглядит:

handleChange(id) {
    this.setState(prevState => {
        const updatedTodos = prevState.todos.map(todo => {
            if (todo.id === id) {
                todo.completed = !todo.completed
            }
            return todo
        })
        return {
            todos: updatedTodos
        }
    })
}

Здесь, в ходе обработки массива с помощью map(), если обнаруживается элемент, id которого равен id, переданному методу handleChange(), значение свойства completed этого элемента меняется на противоположное (true на false и наоборот). После этого, независимо от того, был ли изменён элемент, map() возвращает этот элемент. Он попадает в новый массив (представленный здесь константой updatedTodos) под тем же индексом, под которым соответствующий элемент был в массиве todos из предыдущей версии состояния. После того, как будет просмотрен весь массив и будет полностью сформирован массив updatedTodos, этот массив используется в качестве значения свойства todos объекта, возвращаемого методом setState(), который представляет собой новую версию состояния.

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

Итоги


Сегодня в нашем распоряжении оказалось работающее Todo-приложение, при написании которого было использовано множество изученных нами концепций React. На самом деле, его ещё вполне можно доработать, в частности, стилизовать его и расширить его возможности. Мы ещё вернёмся к нему на одном из следующих занятий.

Уважаемые читатели! Справились ли вы с сегодняшней практической работой?

  • +20
  • 4,3k
  • 2
RUVDS.com
882,00
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

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

    0
    Получается состояние всегда нужно хранить в компоненте родителе? Ведь мы, когда щелкаем по флажку, изменяем состояние только одного экземпляра компонента TodoItem, а перезаписываем состояния всех. Возможно ли хранить состояние каждого из них отдельно и целесообразно ли это? Будет ли дальше в курсе об этом поподробнее? Если нет, то где можно почитать про такие нюансы?
      0
      Но в этом обработчике меняется предыдущий state, пусть и неявно. Массив todos состоит из объектов, в map попадают ссылки на объекты, а не его копии. После прохода map'ом можно посмотреть в консоли, что изменения произошли и в prevState.todos.

      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

      Самое читаемое