# Подробнее о state

В этом разделе мы посмотрим как изменение *state* влияет на компонент и немного "зацепим" *stateless* архитектуру.

## Изменение state вызывает render компонента

Все указано в подзаголовке, предлагаю нам в этом убедиться:

Фрагмент компонента `<Article />`:

```javascript
...
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>
  )
}
...
```

![4 news 4 renders](/files/-LvLnSnD15iNgsTy-aFL)

Очистите консоль, и нажмите подробнее на любой из новостей:

![1 news re-render](/files/-LvLnSnFKVMk587XyJar)

Убедились? Несколько правил:

* **нельзя** вызывать *setState* внутри *render:* "реакт бдит", и если изменилось состояние - начинает перерисовывать компонент - видит что изменилось состояние - начинает перерисовывать компонент...
* render - **дорогостоящая** операция, поэтому внимательно относитесь к тому, где вы вызываете *setState*, и что это за собой влечет. Банальные `console.log` могут вам в этом помочь.

Очевидно, что если перерисовывается родительский компонент, то будут перерисованы и все дочерние компоненты.

В дальнейшем мы изучим разные "стадии жизни" компонента, и убедимся, что во время его "перерисовки" могут выполняться разные дорогостоящие операции и даже ajax-запросы. Пока что, просто убедимся, что вызов *setState* родителя - перерисует дочерние компоненты. Для этого предлагаю создать обработчик *onClick* на фразе "Всего новостей".

Попробуйте сами.

**Задача**: Необходимо добавить компоненту `<News />` свойство состояния - *counter*, в котором будет хранится количество кликов по фразе "всего новостей". То есть обычный автоинкремент. Это свойство нужно выводить после количества новостей в обычном параграфе (`<p>`). Будет выглядеть так:

![всего кликов 0](/files/-LvLnSnHwSduaS6KxCzZ)

В решении важно использовать `this.setState({counter: ++this.state.counter})`, об этом мы подробнее поговорим после решения, которое представлено ниже в виде подсказок и полностью.

Подсказка **#1**: добавьте свойство state в компонент `<News />` для создания начального состояния.

```javascript
...
state = {
  counter: 0
}
...
```

Подсказка **#2**: добавьте обработчик *onClick* с функцией, которая будет увеличивать cчетчик (следовательно, изменять *state*, следовательно, вызывать *this.setState*... ).

**Решение**: Полный код компонента `<News />`

```javascript
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 "перерисовки".

![1 click 4 re-renders](/files/-LvLnSnJ6klfOU4pETUz)

Поговорим о: `this.setState({counter: ++this.state.counter })`

Почему же, было важно использовать именно префиксную запись ++, а не постфиксную? Сначала вспомним теорию:

* **++** перед переменной (префикс) - сначала увеличивает ее на 1, а потом возвращает значение;
* **++** после переменной (постфикс) - сначала вернет значение, а потом увеличит значение переменной;

В таком случае, мы должны были бы потерять всего 1 клик, не так ли? Проверьте в консоли следующим образом: откройте вкладку React в консоли, выберите компонент `<News />`, начните кликать на фразу "Всего новостей"

* Изменяется ли значение counter, если используется префиксная запись?

> Да, изменяется

* Изменяется ли значение counter, если используется постфиксная запись?

> Нет, не изменяется вообще. (убедитесь сами)

Ответ кроется [в официальной документации](https://reactjs.org/docs/react-component.html#setstate):

*setState() - не изменяет this.state немедленно, а создает очередь изменений состояния. Доступ к this.state после вызова метода, потенциально может вернуть имеющееся (что равносильно - бывшее) значение.*

Самое время крикнуть - верните мои деньги назад, и уйти... Но, не все так плачевно. Теперь вы знаете об этой особенности, и будете если что вооружены. Зачем так сделано? Вероятно, для оптимизации работы библиотеки в целом.

Вообще *state* у компонентов используется не часто. С появлянием [flux](https://facebook.github.io/flux/) - подхода (*здесь я прослезился и не стал переписывать в 2k18м году*), коммьюнити стало перемещаться на сторону *stateless* подхода, когда *state* не используется вообще (за исключением редких моментов). Мой любимый игрок данного лагеря - **Redux**, о котором я тоже написал [подробное руководство](https://www.gitbook.com/book/maxfarseer/redux-course-ru/details) на русском (руководство еще не переписано на современный лад, но теорию почитать можно).

Почему сейчас мы не изучаем Redux, стоит ли бросить все и прочитать другой туториал? Определенно - нет. Продолжайте изучение данного курса.

## Ситуация в 2018м году:

* Есть не только Redux, но и MobX и прочие игроки. Однако Redux до сих пор остается моим любимым;
* React для управления состоянием приложения в целом, добавил Context API, о котором мы еще поговорим;
* Чистый flux не используется;

[Исходный код](https://github.com/maxfarseer/react-course-ru-v2/tree/chp8-more-about-state) с console.log'ами и обработчиком кликов на фразе "Всего новостей".


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://max-frontend.gitbook.io/react-course-ru-v2/ispolzovanie-state/podrobnee-o-state.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
