https://habr.com/ru/company/ruvds/blog/440662/- Блог компании RUVDS.com
- JavaScript
- ReactJS
- Разработка веб-сайтов
В сегодняшней части перевода учебного курса по React вам предлагается продолжить работу над Todo-приложением и сделать так, чтобы щелчки по флажкам воздействовали бы на состояние компонента.
→
Часть 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-приложением
Занятие 33. Практикум. TODO-приложение. Этап №6
→
Оригинал
▍Задание
На этом практическом занятии мы продолжим работу над Todo-приложением, сделаем так, чтобы действия пользователя влияли бы на состояние компонента. Речь идёт о том, чтобы у нас появилась возможность отмечать пункты списка дел как выполненные или невыполненные. Ниже приведён код компонента
App
, некоторые заготовки и комментарии, имеющиеся в котором, призваны помочь вам в выполнении задания. Собственно говоря, вот что вам предлагается сегодня сделать:
- Создайте в компоненте
App
обработчик события, который реагирует на изменения флажков (речь идёт о событии onChange
) и соответствующим образом меняет состояние приложения. Пожалуй, это — самая сложная часть сегодняшнего задания. Для того чтобы с ней справиться — обратите внимание на комментарии и заготовки, представленные в коде.
- Передайте соответствующий метод компоненту
TodoItem
.
- В компоненте
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. На самом деле, его ещё вполне можно доработать, в частности, стилизовать его и расширить его возможности. Мы ещё вернёмся к нему на одном из следующих занятий.
Уважаемые читатели! Справились ли вы с сегодняшней практической работой?
