В сегодняшней части перевода курса по React мы предлагаем вам выполнить очередное практическое задание и представляем вашему вниманию рассказ о том, как модифицировать состояние компонентов React.
> Часть 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-приложением
checked
элемента в компоненте TodoItem
, не предусмотрели механизм для взаимодействия с этим элементом в виде обработчика события onChange
. При работе с интерфейсом приложения это выражается в том, что флажки, выводимые на странице, нельзя устанавливать и снимать.checkbox
компонента TodoItem
обработчиком события onChange
, который, на данном этапе работы, достаточно представить в виде функции, которая что-то выводит в консоль.TodoItem
, который хранится в файле TodoItem.js
:import React from "react"
function TodoItem(props) {
return (
<div className="todo-item">
<input type="checkbox" checked={props.item.completed}/>
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
onChange
элементу checkbox
. Вот как это выглядит в коде: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
Checked!
. При этом щелчки по флажкам не приводят к изменению их состояния, но уведомление из консоли, как можно видеть на следующем рисунке, исчезает.create-react-app
, в файле App.js
которого содержится такой код:import React from "react"
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button>Change!</button>
</div>
)
}
}
export default App
index.css
, который подключён в файле index.js
, содержится следующее описание стилей:div {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
h1 {
font-size: 3em;
}
button {
border: 1px solid lightgray;
background-color: transparent;
padding: 10px;
border-radius: 4px;
}
button:hover {
cursor: pointer;
}
button:focus {
outline:0;
}
App
, код которого представлен выше, представляет собой компонент, основанный на классе. Это вполне очевидно, так как нам нужно, чтобы у этого компонента было бы состояние. В коде компонента мы используем конструктор.super()
и инициализируем состояние, записывая в него свойство count
и присваивая ему начальное значение 0
. В методе render()
мы выводим заголовок первого уровня, представляющий значение свойства count
из состояния компонента, а также кнопку со словом Change!
. Всё это отформатировано с помощью стилей.count
. При этом мы уже изучили методику обработки событий в React, и наша задача сводится к тому, чтобы создать механизм, который, реагируя на щелчок по кнопке, меняет свойство состояния count
.onClick
, который, для начала, будет просто выводить что-нибудь в консоль.click
, назовём его handleClick()
. Вот как теперь будет выглядеть код компонента App
.import React from "react"
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
}
handleClick() {
console.log("I'm working!")
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.handleClick}>Change!</button>
</div>
)
}
}
export default App
render()
, мы используем конструкцию вида this.handleClick
.handleClick()
? Скажем, что если переписать этот метод так:handleClick() {
this.state.count++
}
React.Component
. Это — метод setState()
. Его используют в тех случаях, когда нужно изменить состояние компонента. Этим методом можно пользоваться по-разному.setState()
объект, который заменит состояние. Перепишем метод handleClick()
так:handleClick() {
this.setState({ count: 1 })
}
TypeError: Cannot read property 'setState' of undefined
. На самом деле, то о чём мы сейчас говорим, вызывает множество споров в среде React-разработчиков, и сейчас я собираюсь показать вам очень простой способ решения этой проблемы, который, на первый взгляд, может показаться необычным.handleClick()
в нашем случае), в котором планируется использовать метод setState()
, этот метод нужно связать с this
. Делается это в конструкторе. Код компонента после этой модификации будет выглядеть так:import React from "react"
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({ count: 1 })
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.handleClick}>Change!</button>
</div>
)
}
}
export default App
Change!
над ней появится число 1, сообщений об ошибках выводиться не будет.0
меняется на 1
, а если щёлкнуть по ней ещё раз — ничего уже не произойдёт. В общем-то, это и неудивительно. Код, вызываемый при щелчке по кнопке, делает своё дело, каждый раз меняя состояние на новое, правда, после первого же щелчка по кнопке новое состояние, в котором в свойстве count
хранится число 1
, не будет отличаться от старого. Для того чтобы решить эту проблему, рассмотрим ещё один способ работы с методом setState()
.count
, которое хранится в предыдущей версии состояния, хотим прибавить к этому значению 1
. В случаях, когда для изменения состояния нужно быть в курсе того, что в нём хранилось ранее, методу setState()
можно передать функцию, которая, в качестве параметра, получает предыдущую версию состояния. Назвать этот параметр можно как угодно, в нашем случае это будет prevState
. Заготовка этой функции будет выглядеть так:handleClick() {
this.setState(prevState => {
})
}
this.state
, но такой подход нас не устроит. Поэтому важно, чтобы эта функция принимала бы предыдущую версию состояния компонента.handleClick()
, решающий эту задачу:handleClick() {
this.setState(prevState => {
eturn {
count: prevState.count + 1
}
})
}
count
мы используем конструкцию count: prevState.count + 1
. Можно подумать, что тут подойдёт и конструкция вида count: prevState.count++
, но оператор ++
приводит к модификации переменной, к которой он применяется, это будет означать попытку модификации предыдущей версии состояния, поэтому им мы здесь не пользуемся.import React from "react"
class App extends React.Component {
constructor() {
super()
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState(prevState => {
return {
count: prevState.count + 1
}
})
}
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick={this.handleClick}>Change!</button>
</div>
)
}
}
export default App
render()
. В результате дочерний компонент будет отражать новые данные, хранящиеся в состоянии родительского компонента.К сожалению, не доступен сервер mySQL