# Оптимизация перерисовок

У нас есть 2 компонента `<User />` и `<Page />`. Мы специально сделали для них два редьюсера, чтобы обновлять их независимо! А у нас? А у нас `<User />` каждый раз обновляется при обновлении `<Page />` и наоборот.

Добавьте console.log в render метод у `<User />`:

*src/components/User.js*

```jsx
...
render() {
  console.log('<User/> render')
  return <div className="ib user">{this.renderTemplate()}</div>
}
...
```

![user-render-console-log](/files/-LvLnQOgoxfmkHKpza-0)

Перерисовка компонента User происходит постоянно. Это не влияет на производительность нашего мини-приложения, однако, мы не готовы с этим мириться.

Представьте дашборд (панель) с большим количеством виджетов, информация в которых обновляется по событиям от бэкэнда. Если каждый виджет будет перерисовывать полностью весь дашборд, то это будет крайне некрасиво (как для юзера, так и для производительности).

Чтобы такого не было, мы должны каждую отдельную сущность приложения класть в отдельный контейнер.

Будем исправлять, для этого:

* `<App />` становится тупым компонентом, который рендерит 2 контейнера:
  * `<PageContainer />`
  * `<UserContainer />`

Данные контейнеры - просто обертки над нашими компонентами, в которых мы "подключаемся (*connect*) к Redux".

Так же, я сразу заменю название у экшенов внутри mapDispatchToProps: уберу оттуда частичку *Action*.

В остальном, мы просто "разносим" то, что было в `<App />` по раздельным контейнерам.

*src/index.js*

```jsx
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { store } from './store/configureStore'
import App from './components/App' // изменили путь

import registerServiceWorker from './registerServiceWorker'

import './index.css'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)
registerServiceWorker()
```

*src/components/App.js*

```jsx
import React, { Component } from 'react'
import UserContainer from '../containers/UserContainer' // изменили импорт
import PageContainer from '../containers/PageContainer' // изменили импорт

class App extends Component {
  render() {
    return (
      <div className="app">
        <PageContainer />
        <UserContainer />
      </div>
    )
  }
}

export default App
```

*src/containers/PageContainer.js*

```jsx
import React from 'react'
import { connect } from 'react-redux'
import { Page } from '../components/Page'
import { getPhotos } from '../actions/PageActions'

class PageContainer extends React.Component {
  render() {
    const { page, getPhotos } = this.props
    return (
      <Page
        photos={page.photos}
        year={page.year}
        isFetching={page.isFetching}
        error={page.error}
        getPhotos={getPhotos}
      />
    )
  }
}

const mapStateToProps = store => {
  return {
    page: store.page,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    getPhotos: year => dispatch(getPhotos(year)),
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(PageContainer)`
```

Как вы могли заметить, все что касалось `<Page />` хранится в отдельном контейнере: подписка на часть стора, экшен, пропсы...

То же самое, делаем для `<UserContainer />`

*src/containers/UserContainer.js*

```jsx
import React from 'react'
import { connect } from 'react-redux'
import { User } from '../components/User'
import { handleLogin } from '../actions/UserActions'

class UserContainer extends React.Component {
  render() {
    const { user, handleLogin } = this.props
    return (
      <User
        name={user.name}
        error={user.error}
        isFetching={user.isFetching}
        handleLogin={handleLogin}
      />
    )
  }
}

const mapStateToProps = store => {
  return {
    user: store.user,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    handleLogin: () => dispatch(handleLogin()),
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(UserContainer)
```

Теперь внимание: в компоненте *App* есть два "независимых компонента". Сейчас при изменении данных в редьюсере для *Page* - *User* перерисовываться не будет. *App* тоже, само собой. *App* у нас вообще не будет перерисовываться более при таком раскладе.

Снова покликаем по кнопкам (*console.log* в `<User />` остался):

![user-not-re-renders-console-log](/files/-LvLnQOoeajCIQbueqYz)

Еще раз заострю внимание: мы не просто сделали хорошо, мы сделали супер-хорошо! Render - обычно самая дорогая операция. Вызывать "перерисовку" каждого "кусочка" приложения нужно осознанно. Всегда проверяйте (например, так же банально с помощью `console.log`) сколько раз у вас что рендерится, и нет ли чего лишнего.

Давайте заодно здесь быстренько исправим отрисовку кнопок в `<Page />`

*src/components/Page.js*

```jsx
...

export class Page extends React.Component {
  onBtnClick = e => {
    ...
  }
  renderButtons = () => {
    const years = [2018, 2017, 2016, 2015, 2014]

    return years.map((item, index) => { // [1]
      return (
        <button key={index} className="btn" onClick={this.onBtnClick}>
          {item}
        </button>
      )
    })
  }
  renderTemplate = () => {
    ...
  }

  render() {
    const { year, photos } = this.props
    return (
      <div className="ib page">
        <p>{this.renderButtons()}</p>
        <h3>
          {year} год [{photos.length}]
        </h3>
        {this.renderTemplate()}
      </div>
    )
  }
}
...
```

(*Добавьте по вкусу щепотку `margin` для `.btn`*)

\[1] Использовать в данной ситуации *index* для *key* плохо?. В данном случае - не плохо. Напоминаю, что индекс в качестве ключа плохо использовать, когда у вас элементы меняются местами. Справедливости ради, здесь в качестве индекса можно было бы использовать "год", так как главное в индексе - это **уникальность**.

**Итого**: мы научились бережно относится к перерисовкам, а так же закрепили на практике вопрос зачем разбивать редьюсер на маленькие редьюсеры.

[Исходный код](https://github.com/maxfarseer/redux-course-ru-v2/tree/chp13-optimize-re-renders).


---

# 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/redux-course-ru-v2/sozdanie/optimizatsiya-refaktoring/optimizatsiya-pererisovok.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.
