Подробнее о state
Подробнее о state
В этом разделе мы посмотрим как изменение state влияет на компонент и немного "зацепим" stateless архитектуру.
Изменение state вызывает render компонента
Все указано в подзаголовке, предлагаю нам в этом убедиться:
Фрагмент компонента <Article />
:
... render() { const { author, text, bigText } = this.props.data const { visible } = this.state console.log('render', this); // добавили console.log return ( <div className='article'> <p className='news__author'>{author}:</p> <p className='news__text'>{text}</p> { !visible && <a onClick={this.handleReadMoreClck} href="#" className='news__readmore'>Подробнее</a> } { visible && <p className='news__big-text'>{bigText}</p> } </div> ) } ...
Очистите консоль, и нажмите подробнее на любой из новостей:
Убедились? Несколько правил:
нельзя вызывать setState внутри render: "реакт бдит", и если изменилось состояние - начинает перерисовывать компонент - видит что изменилось состояние - начинает перерисовывать компонент...
render - дорогостоящая операция, поэтому внимательно относитесь к тому, где вы вызываете setState, и что это за собой влечет. Банальные
console.log
могут вам в этом помочь.
Очевидно, что если перерисовывается родительский компонент, то будут перерисованы и все дочерние компоненты.
В дальнейшем мы изучим разные "стадии жизни" компонента, и убедимся, что во время его "перерисовки" могут выполняться разные дорогостоящие операции и даже ajax-запросы. Пока что, просто убедимся, что вызов setState родителя - перерисует дочерние компоненты. Для этого предлагаю создать обработчик onClick на фразе "Всего новостей".
Попробуйте сами.
Задача: Необходимо добавить компоненту <News />
свойство состояния - counter, в котором будет хранится количество кликов по фразе "всего новостей". То есть обычный автоинкремент. Это свойство нужно выводить после количества новостей в обычном параграфе (<p>
). Будет выглядеть так:
В решении важно использовать this.setState({counter: ++this.state.counter})
, об этом мы подробнее поговорим после решения, которое представлено ниже в виде подсказок и полностью.
Подсказка #1: добавьте свойство state в компонент <News />
для создания начального состояния.
... state = { counter: 0 } ...
Подсказка #2: добавьте обработчик onClick с функцией, которая будет увеличивать cчетчик (следовательно, изменять state, следовательно, вызывать this.setState... ).
Решение: Полный код компонента <News />
class News extends React.Component { state = { counter: 0, // добавили свойство counter (счетчик) } handleCounter = () => { // добавили новый метод this.setState({ counter: ++this.state.counter }) // в котором увеличиваем счетчик } renderNews = () => { const { data } = this.props let newsTemplate = null if (data.length) { newsTemplate = data.map(function(item) { return <Article key={item.id} data={item}/> }) } else { newsTemplate = <p>К сожалению новостей нет</p> } return newsTemplate } render() { const { data } = this.props const { counter } = this.state // вытащили counter return ( <div className='news'> {this.renderNews()} { /* добавили onClick */ data.length ? <strong onClick={this.handleCounter} className={'news__count'}>Всего новостей: {data.length}</strong> : null } <p>Всего кликов: { counter }</p> </div> ); } }
Проверьте в браузере. Если вы не удаляли console.log из компонента <Article />
- на каждый клик по фразе "Всего новостей", в консоли будет появляться по 4 "перерисовки".
Поговорим о: this.setState({counter: ++this.state.counter })
Почему же, было важно использовать именно префиксную запись ++, а не постфиксную? Сначала вспомним теорию:
- ++ перед переменной (префикс) - сначала увеличивает ее на 1, а потом возвращает значение;
- ++ после переменной (постфикс) - сначала вернет значение, а потом увеличит значение переменной;
В таком случае, мы должны были бы потерять всего 1 клик, не так ли? Проверьте в консоли следующим образом: откройте вкладку React в консоли, выберите компонент <News />
, начните кликать на фразу "Всего новостей"
- Изменяется ли значение counter, если используется префиксная запись?
Да, изменяется
- Изменяется ли значение counter, если используется постфиксная запись?
Нет, не изменяется вообще. (убедитесь сами)
Ответ кроется :
setState() - не изменяет this.state немедленно, а создает очередь изменений состояния. Доступ к this.state после вызова метода, потенциально может вернуть имеющееся (что равносильно - бывшее) значение.
Самое время крикнуть - верните мои деньги назад, и уйти... Но, не все так плачевно. Теперь вы знаете об этой особенности, и будете если что вооружены. Зачем так сделано? Вероятно, для оптимизации работы библиотеки в целом.
Вообще state у компонентов используется не часто. С появлянием - подхода (здесь я прослезился и не стал переписывать в 2k18м году), коммьюнити стало перемещаться на сторону stateless подхода, когда state не используется вообще (за исключением редких моментов). Мой любимый игрок данного лагеря - Redux, о котором я тоже написал на русском (руководство еще не переписано на современный лад, но теорию почитать можно).
Почему сейчас мы не изучаем Redux, стоит ли бросить все и прочитать другой туториал? Определенно - нет. Продолжайте изучение данного курса.
Ситуация в 2018м году:
- Есть не только Redux, но и MobX и прочие игроки. Однако Redux до сих пор остается моим любимым;
- React для управления состоянием приложения в целом, добавил Context API, о котором мы еще поговорим;
- Чистый flux не используется;
с console.log'ами и обработчиком кликов на фразе "Всего новостей".
- 1. Вступление
- 2. От автора
- 3. Подключаем react как script
- 4. Создание компонента
- 5. Использование props
- 6. If-else, тернарный оператор
- 7. Порефакторим...
- 8. Prop-types
- 9. Использование state
- 10. Подробнее о state
- 11. Работа с input
- 12. Жизненный цикл компонента
- 13. Работа с формой
- 14. Добавить новость
- 15. Итоги по основам
- 16. create-react-app
- 17. Приборка и импорты
- 18. Асинхронные запросы
- 19. Спам-фильтр
- 20. componentWillReceiveProps
- 21. getDerivedStateFromProps
- 22. Порефакторим...
- 23. Заключение
Комментариев: 0