Введение в PureComponent
предыдущий постРешение для оптимизации производительности PureRenderMixin of React, заключается в переписывании и оптимизации метода реакции shouldComponentUpdate. Но поскольку в React15.3 добавлен класс PureComponent, его легко использовать в ваших собственных компонентах, вам нужно всего лишь заменить Component на PureComponent, поэтому автор оригинала рекомендует использовать PureComponent сегодня.
Принцип чистого компонента
Мы знаем, что изменение состояния и реквизита компонента вызовет рендеринг, но состояние и реквизиты компонента не изменились, и рендер не будет выполнен.Только когда PureComponent обнаружит, что состояние или реквизиты изменились, PureComponent вызовет метод рендеринга, поэтому цель повышения производительности может быть достигнута без ручного написания дополнительных проверок. На самом деле react делает самое поверхностное сравнение:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
shadowEqual только поверхностно проверяет реквизиты и состояние компонента, поэтому вложенные объекты и массивы не сравниваются. Глубокое сравнение заключается в обходе соответствующих ключевых значений перечисления слой за слоем для сравнения, что является пустой тратой времени. Если сравнение глубокое, вы также можете использовать shouldComponentUpdate, чтобы вручную сравнить, требуется ли повторная визуализация. Самый простой способ — сравнить props или state напрямую:
shouldComponentUpdate(nextProps, nextState) {
return nextProps.xxx.xx === props.xxx.xx;
}
Кроме того, вы можете использовать неизменяемое свойство, Immutable.js — это неизменяемая библиотека. В этом случае сравнение свойств очень просто, потому что существующий объект не изменяется, а создается новый объект заново.
Использование чистых компонентов
Восхождение на яму 1: изменчивые данные не могут использовать ссылку
PureComponent экономит наше время и избавляет от лишнего кода. Что ж, очень важно научиться правильно им пользоваться, иначе при неправильном использовании ничего не получится. Например, рассмотрим ситуацию, когда у родительского компонента есть метод рендеринга и обработчик кликов:
handleClick() {
let {items} = this.state
items.push('new-item')
this.setState({ items })
}
render() {
return (
<div>
<button onClick={this.handleClick} />
<ItemList items={this.state.items} />
</div>
)
}
Поскольку ItemList является чистым компонентом, он не будет отображаться при нажатии, но мы добавляем новое значение в this.state.items, но оно по-прежнему указывает на ссылку на тот же объект. Однако это можно легко изменить, удалив изменяемый объект, чтобы он отображался правильно.
handleClick() {
this.setState(prevState => ({
words: prevState.items.concat(['new-item'])
}))
}
Восхождение на яму 2: используйте ссылку для неизменяемых данных
В приведенном выше случае, когда изменчивые данные нельзя использовать со ссылкой, есть операция удаления клика, если мы удалим такой код:
constructor(props){
super(props)
this.state={
items: [{a: 1}, {a: 2}, {a: 3}]
}
}
handleClick = () => {
const { items } = this.state;
items.splice(items.length - 1, 1);
this.setState({ items });
}
Ссылка на элементы также изменяется, но если в элементах есть данные ссылочного типа, то state.items[0] === nextState.items[0] имеет значение false, а дочерний компонент все равно перерисовывается. Это требует от нас гарантировать, что ссылка на неизменяемые данные подкомпонента не может быть изменена. В настоящее время можно использовать упомянутую выше библиотеку функций immutable-js.
Восхождение на яму 3: функция обратного вызова от родителя к ребенку передается через реквизит
Когда мы передаем функцию обратного вызова в дочерне-родительском общении:
// step1
<MyInput onChange={e => this.props.update(e.target.value)} />
// step2
update(e) {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update.bind(this)} />
}
Поскольку каждая операция рендеринга свойства onChange компонента MyInput будет возвращать новую функцию, поскольку ссылка отличается, рендеринг родительского компонента также вызовет рендеринг компонента MyInput , даже если изменений нет, поэтому нужно максимально избегать такого написания, лучше всего инкапсулировать:
update = (e) => {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update} />
}
Восхождение на яму 4: новый массив, пустой массив также приведет к повторному рендерингу компонента.
<Entity values={this.props.values || []}/>
Чтобы избежать этой проблемы, вы можете использовать defaultProps, который содержит начальное пустое состояние свойства. Когда создается PureComponent, поскольку создается новый объект функции, он получает новые данные и выполняет повторный рендеринг. Самый простой способ решить эту проблему: использовать bind в методе конструктора компонента.
constructor(props) {
super(props)
this.update = this.update.bind(this)
}
update(e) {
this.props.update(e.target.value)
}
render() {
return <MyInput onChange={this.update} />
}
Кроме того, в JSX проверка smallEqual всегда будет возвращать false для любого компонента, содержащего дочерние элементы.
Как использовать ЧистыйКомпонент
Использование простое:
import React { PureComponent, Component } from 'react';
class Foo extends (PureComponent || Component) {
//...
}
Старая версия совместима с методом записи, и в старой версии React не будет сообщено об ошибке.
Суммировать
1. Когда чистый компонент игнорирует повторный рендеринг, он будет влиять не только на себя, но и на свои подэлементы, поэтому лучшим случаем использования PureComponent является отображение компонентов, которые не имеют ни подкомпонентов, ни зависят от глобального состояние приложения.
2. PureComponent действительно работает только на некоторых чистых компонентах дисплея.Неважно, используются ли сложные компоненты.В любом случае, smallEqual не может пройти тест, но помните, что props и state не могут использовать одну и ту же ссылку.