State - состояние React компонента
Классы в React используем в тех случаях, когда нам нужно хранить внутреннее состояние компонента, которое изменяется в ходе выполнения программы. Такое состояние называется State. State очень похоже на Props - свойства, разница только в том, что State можно изменить, а Props - нет.
State это всегда объект.
Хранить State можно в конструкторе
export default class TodoListItem extends Component {
constructor() {
super();
this.state = {
done: false
}
}
}
Или при помощи полей класса (экспериментальное свойство пока ещё не вошедшее в стандарт).
Дополнено: уже вошедшее
export default class TodoListItem extends Component {
state = {
done: false
};
}
Напишем код. который позволяет в зависимости от значения done добавлять к пунктам списка дел отметку о выполнении. Для этого достаточно присвоить им класс done, для которого в css указаны соответствующие стили.
1. Деструктурируем state
const { done } = this.state;
2. Создаём переменную classNames
let classNames = 'todo-list-item';
Указываем, что именно указанные в ней классы получает пункт списка дел
<span className={classNames}>
3. Добавляем условие: если done === true, к classNames добавляем класс done
if (done){
classNames += ' done';
}
4. Для пункта списка, по которому кликнули, меняем значение done. Изменить state напрямую нельзя. для этого используется метод setState()
onLabelClick = () => {
this.setState({
done: true
})
};
Код полностью
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import './TodoListItem.css';
export default class TodoListItem extends Component {
state = {
done: false
};
onLabelClick = () => {
this.setState({
done: true
})
};
render() {
const { label, important = false } = this.props;
const { done } = this.state;
let classNames = 'todo-list-item';
if (done){
classNames += ' done';
}
return (
<span className={classNames}>
<span
className="todo-list-item-label"
onClick = {this.onLabelClick}>
{label}
</span>
</span>
);
};
}
Добавим ещё один класс , которым будем отмечать важные дела. Добавлять его будем при клике на кнопку
export default class TodoListItem extends Component {
state = {
done: false,
important: false
};
onLabelClick = () => {
this.setState({
done: true
})
};
onButtonClick = () => {
this.setState({
important: true
})
};
render() {
const { label } = this.props;
const { done, important } = this.state;
let classNames = 'todo-list-item';
if (done){
classNames += ' done';
}
if (important){
classNames += ' important';
}
const style = {
color: important ? 'steelblue' : 'black',
fontWeight: important ? 'bold' : 'normal'
};
return (
<span className={classNames}>
<span
className="todo-list-item-label"
onClick = {this.onLabelClick}>
{label}
</span>
<button type="button"
className="btn btn-outline-success btn-sm float-right"
onClick = {this.onButtonClick}>
<i className="fa fa-exclamation" />
</button>
</span>
);
};
}
Здесь важно запомнить один момент: для изменении setState() достаточно изменить не весь объект state целиком, а только одно нужное нам свойство.
Асинхронность state
Предположим, что мы хотим дать пользователю возможность изменять состояние пункта дел с важного на неважное и наоборот.Указанный ниже вариант работающий, но неверный
onButtonClick = () => {
this.setState({
important: !this.state.important
})
};
здесь мы меняем состояние state при клике на кнопку.
Но метод setState() иногда (но не всегда) может работать асинхронно.
То есть state обновляется не мгновенно .
Если нам нужно изменить значение state только после того, как оно уже обновилось, функция должна выглядеть следующим образом:
onButtonClick = () => {
this.setState((state) => {
return {
important: !state.important
};
})
};
Указанный в параметрах функции state говорит реакту, что функцию нужно выполнить только тогда, когда значение state уже установлено.
Небольшой рефракторинг. state можно сразу деструктурировать, в результате код немного изменится
onButtonClick = () => {
this.setState(({important}) => {
return {
important: !important
};
})
};