От автора: как только вы приступите к изучению React, вы столкнетесь с понятием state. State имеет огромное значение в React. Возможно, это первая причина, почему вы стали изучать React. Давайте рассмотрим понятие React state и принцип его работы.
Что такое state?
State (состояние) в React – это объект простого JS, позволяющий отслеживать данные компонента. Состояние компонента может меняться. Смена состояния компонента зависит от функциональности приложения. Изменения могут основываться на ответе от пользователя, новых сообщениях с сервера, ответа сети и т.д.
Состояние компонента должно быть приватным для компонента и контролироваться им. Изменения состояния компонента необходимо делать внутри компонента – инициализация и обновление состояния компонента.
Компоненты класса
Состояния доступны только для компонентов класса. Главная причина, почему вы захотите использовать компоненты класса, а не функциональные компоненты заключается в том, что компоненты класса могут обладать состоянием. Давайте разберемся, в чем разница. Функциональные компоненты – это JS функции:
1 2 3 4 5 6 7 |
const App = (props) => { return ( <div> { this.props } </div> ) } |
Если вам необходима такая простая функциональность, как сверху, то функциональные компоненты подойдут идеально. Компонент класса намного сложнее.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class App extends React.Component { constructor(props) { super(props) this.state = { username: 'johndoe' } } render() { const { username } = this.state return( <div> { username } </div> ) } } |
Выше я устанавливаю состояние username компонента в строку.
Конструктор
Судя по официальной документации, инициализация состояния происходит в конструкторе. Инициализация проходит через установку this.state в объект, как сверху. Помните: state – это объект простого JS. Первичное состояние компонента App было задано в объект state, в котором хранится ключ username и значение johndoe (this.state = { username: ‘johndoe’ }).
Инициализация состояния компонента может быть настолько сложной:
1 2 3 4 5 6 7 8 9 10 |
constructor(props) { super(props) this.state = { currentTime: 0, status: false, btnOne: false, todoList: [], name: 'John Doe' } } |
Доступ к состоянию
Доступ к инициализированному состоянию можно получить в методе render(), как я сделал сверху.
1 2 3 4 5 6 7 8 |
render() { const { username } = this.state return( <div> { username } </div> ) } |
Другой способ:
1 2 3 4 5 6 7 |
render() { return( <div> { this.state.username } </div> ) } |
Разница в том, что в первом примере я извлек username из состояния, но его можно записать через const status = this.state.username. Благодаря деструктуризации ES6, я не обязан идти таким путем. Не смущайтесь, когда видите такое. Важно знать, что когда я делал это, я не переназначал состояние. Первичные настройки состояния были проведены в конструкторе. Повторно их делать не нужно – никогда не обновляйте состояние компонента напрямую.
Обратиться к состоянию можно this.state.property-name. Не забывайте, что после инициализации состояния для использования this.state необходимо получить доступ к состоянию.
Обновление состояния
Единственный допустимый способ обновления состояния компонента – через setState(). Разберем это на примере. Во-первых, начну с создания метода, который будет вызываться для обновления username компонента. Этот метод должен получить аргумент, который будет использовать для обновления состояния.
1 2 3 |
handleInputChange(username) { this.setState({username}) } |
Еще раз, обратите внимание, что я передаю объект в setState(). Далее мне необходимо передать эту функцию в обработчик события, который вызывается при изменении значения поля ввода. Обработчик события предоставит контекст события, которое было вызвано, что позволяет получить значение, введенное в поле, через event.target.value. Это аргумент, переданный в метод handleInputChange(). Метод render должен выглядеть следующим образом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
render() { const { username } = this.state return ( <div> <div> <input type="text" value={this.state.username} onChange={event => this.handleInputChange(event.target.value)} /> </div> <p>Your username is, {username}</p> </div> ) } |
При каждом вызове setState() в React отправляется запрос на обновление DOM через обновленное состояние. Теперь вы понимаете, что обновление состояния можно отложить.
Компонент должен выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class App extends React.Component { constructor(props) { super(props) this.state = { username: 'johndoe' } } handleInputChange(username) { this.setState({username}) } render() { const { username } = this.state return ( <div> <div> <input type="text" value={this.state.username} onChange={event => this.handleInputChange(event.target.value)} /> </div> <p>Your username is, {username}</p> </div> ) } } |
Передача состояния в виде свойств
Состояние можно передавать в виде свойств от родителя к дочернему компоненту. Создадим новый компонент для создания списка напоминаний. У компонента будет поле ввода ежедневных задач, задачи будут передаваться как свойства в дочерний компонент.
Попробуйте создать родительский компонент сами, используя полученные знания.
Начнем с создания первичного состояния компонента.
1 2 3 4 5 6 7 8 9 |
class App extends React.Component { constructor(props) { super(props) this.state = { todoList: [] } } render() { return() } } |
У состояния компонента есть todoList, заданный в пустой массив. В методе render() я хочу возвращать форму для отправки задач.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
render() { const { todoList } = this.state return ( <div> <h2>Enter your to-do</h2> <form onSubmit={this.handleSubmit}> <label>Todo Item</label> <input type="text" name="todoitem" /> <button type="submit">Submit</button> </form> </div > ) } |
После ввода и нажатия на кнопку отправки будет вызываться метод handleSubmit. Этот метод будет обновлять состояние компонента. Я буду обновлять состояние путем добавления нового значения в todoList массив через concat. Так будет устанавливаться значение для todoList внутри метода setState(). Как это выглядит:
1 2 3 4 5 6 7 |
handleSubmit = (event) => { event.preventDefault() const value = (event.target.elements.todoitem.value) this.setState(({todoList}) => ({ todoList: todoList.concat(value) })) } |
При каждом клике на кнопку отправки получается контекст события. Останавливать стандартное действие отправки, которое перезагружает страницу, будем через event.preventDefault(). Введенное в поле значение присваивается переменной value, которая потом передается как аргумент при вызове todoList.concat(). React обновляет состояние todoList через добавление нового значения в первичный пустой массив. Этот новый массив становится текущим состоянием todoList. Цикл повторяется при добавлении нового элемента.
Цель – передать отдельный элемент в дочерний компонент в виде свойства. Для этого урока будем называть компонент TodoItem. Вставьте код ниже в родительский div внутри метода render().
1 2 3 4 |
<div> <h2>Your todo lists include:</h2> { todoList.map(i => <TodoItem item={i} /> )} </div> |
Мы проходимся в цикле по todoList через map. То есть в компонент TodoItem передается один элемент в виде свойства. Для этого вам нужен компонент TodoItem, который получает свойство и рендерит его в DOM. Я покажу, как сделать это через функциональные компоненты и компоненты класса.
Функциональный компонент:
1 2 3 4 5 6 7 |
const TodoItem = (props) => { return ( <div> {props.item} </div> ) } |
Компонент класса:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class TodoItem extends React.Component { constructor(props) { super(props) } render() { const {item} = this.props return ( <div> {item} </div> ) } } |
Если в этом компоненте не нужно управлять состоянием, лучше использовать функциональный компонент.
Повышение уровня
При разработке React приложений вы часто будете обрабатывать состояние. Изучив описанные выше темы вы должны быть уверены в том, что сможете погрузиться в продвинутую часть управления состоянием в React. Для более продвинутого изучения рекомендую официальную документацию React State and Lifecycle, а также Uber React Guide on Props vs State.
Автор: Kingsley Silas
Источник: //css-tricks.com/
Редакция: Команда webformyself.