Оптимизация перерисовок
У нас есть 2 компонента <User />
и <Page />
. Мы специально сделали для них два редьюсера, чтобы обновлять их независимо! А у нас? А у нас <User />
каждый раз обновляется при обновлении <Page />
и наоборот.
Добавьте console.log в render метод у <User />
:
src/components/User.js
...
render() {
console.log('<User/> render')
return <div className="ib user">{this.renderTemplate()}</div>
}
...

Перерисовка компонента User происходит постоянно. Это не влияет на производительность нашего мини-приложения, однако, мы не готовы с этим мириться.
Представьте дашборд (панель) с большим количеством виджетов, информация в которых обновляется по событиям от бэкэнда. Если каждый виджет будет перерисовывать полностью весь дашборд, то это будет крайне некрасиво (как для юзера, так и для производительности).
Чтобы такого не было, мы должны каждую отдельную сущность приложения класть в отдельный контейнер.
Будем исправлять, для этого:
<App />
становится тупым компонентом, который рендерит 2 контейнера:<PageContainer />
<UserContainer />
Данные контейнеры - просто обертки над нашими компонентами, в которых мы "подключаемся (connect) к Redux".
Так же, я сразу заменю название у экшенов внутри mapDispatchToProps: уберу оттуда частичку Action.
В остальном, мы просто "разносим" то, что было в <App />
по раздельным контейнерам.
src/index.js
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
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
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
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 />
остался):

Еще раз заострю внимание: мы не просто сделали хорошо, мы сделали супер-хорошо! Render - обычно самая дорогая операция. Вызывать "перерисовку" каждого "кусочка" приложения нужно осознанно. Всегда проверяйте (например, так же банально с помощью console.log
) сколько раз у вас что рендерится, и нет ли чего лишнего.
Давайте заодно здесь быстренько исправим отрисовку кнопок в <Page />
src/components/Page.js
...
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 плохо?. В данном случае - не плохо. Напоминаю, что индекс в качестве ключа плохо использовать, когда у вас элементы меняются местами. Справедливости ради, здесь в качестве индекса можно было бы использовать "год", так как главное в индексе - это уникальность.
Итого: мы научились бережно относится к перерисовкам, а так же закрепили на практике вопрос зачем разбивать редьюсер на маленькие редьюсеры.
Last updated
Was this helpful?