Реагировать функциональный компонент без состояния, PureComponent, Component; Какие различия и когда мы должны использовать что?

81

Пришло время узнать, что из React v15.3.0 у нас есть новый базовый класс с именем PureComponent для расширения с помощью PureRenderMixin. Я понимаю, что под капотом это использует неглубокое сравнение реквизитов внутри shouldComponentUpdate.

Теперь у нас есть 3 способа определить компонент React:

  • Функциональный компонент без состояния, который не расширяет класс
  • Компонент, расширяющий PureComponent класс
  • Нормальный компонент, расширяющий Component класс

Некоторое время назад мы использовали для вызова компонентов без состояния как Pure Components, или даже Dumb Components. Похоже, что все определение слова "чистый" теперь изменилось в "Реактите".

Хотя я понимаю основные различия между этими тремя, я все еще не уверен, что , когда выбрать, что. Также каковы последствия воздействия и компромиссы каждого из них?


Обновление

Это вопрос, который я ожидаю, чтобы получить разъяснение:

  • Должен ли я выбрать, чтобы определить простые компоненты как функциональные (для простоты) или расширить класс PureComponent (для достижения максимальной производительности)?
  • Является ли повышение производительности, что я получаю реальный компромисс для простота, которую я потерял?
  • Должен ли я когда-либо использовать обычный класс Component, когда я всегда могу использовать PureComponent для повышения производительности?
Теги:
ecmascript-6

2 ответа

151
Лучший ответ

Как вы решаете, как вы выбираете эти три на основе цели/размера/реквизита/поведения наших компонентов?

Расширение от React.PureComponent или React.Component с помощью настраиваемого метода shouldComponentUpdate имеет последствия для производительности. Использование функциональных компонентов без состояния является "архитектурным" выбором и не имеет каких-либо преимуществ по производительности из коробки (пока).

  • Для простых, только для презентаций компонентов, которые необходимо легко использовать повторно, предпочитайте функциональные компоненты без состояния. Таким образом, вы уверены, что они отделены от реальной логики приложения, что они мертвы - легко тестируются и что у них нет неожиданных побочных эффектов. Исключение составляет то, что по какой-то причине у вас их много или вам действительно нужно оптимизировать свой метод рендеринга (поскольку вы не можете определить shouldComponentUpdate для функционального компонента без состояния).

  • Расширьте PureComponent, если вы знаете, что ваш результат зависит от простого реквизита/состояния ( "простой" означает отсутствие вложенных структур данных, поскольку PureComponent выполняет неглубокое сравнение). И вам нужно/может получить некоторые улучшения производительности.

  • Расширьте Component и реализуйте свой собственный shouldComponentUpdate, если вам нужно получить некоторые выгоды от производительности, выполнив собственную логику сравнения между следующими/текущими реквизитами и состоянием. Например, вы можете быстро выполнить глубокое сравнение с помощью lodash # isEqual:

    class MyComponent extends Component {
        shouldComponentUpdate (nextProps, nextState) {
            return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
        }
    }
    

Кроме того, реализация вашего собственного shouldComponentUpdate или расширение от PureComponent - это оптимизация, и, как обычно, вы должны начать изучать это, только если у вас есть проблемы с производительностью (избегать преждевременных оптимизаций). Как правило, я всегда стараюсь делать эти оптимизации после того, как приложение находится в рабочем состоянии, причем большинство функций уже реализованы. Гораздо проще сосредоточиться на проблемах производительности, когда они действительно мешают.

Подробнее

Функциональные компоненты без состояния:

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

Плюсы:

  • Самый простой способ определения компонента в реактиве. Если вам не нужно управлять каким-либо государством, зачем беспокоиться о классах и наследовании? Одно из главных отличий между функцией и классом состоит в том, что с помощью функции вы уверены, что вывод зависит только от ввода (не от истории предыдущих исполнений).

  • В идеале в вашем приложении вы должны стремиться иметь как можно больше элементов без состояния, потому что это обычно означает, что вы переместили свою логику за пределы уровня представления и переместили ее на что-то вроде redux, что означает, что вы можете протестировать свою реальную логики без необходимости делать что-либо (гораздо проще тестировать, многократно использовать и т.д.).

Минусы:

  • Нет методов жизненного цикла. У вас нет возможности определить componentDidMount и других друзей. Обычно вы делаете это в родительском компоненте выше в иерархии, чтобы вы могли превратить всех детей в безстоящие.

  • Никакой способ ручного управления, когда требуется повторная визуализация, поскольку вы не можете определить shouldComponentUpdate. Повторное рендеринг происходит каждый раз, когда компонент получает новые реквизиты (нет возможности для мелкого сравнения и т.д.). В будущем React может автоматически оптимизировать компоненты без гражданства, так как теперь есть некоторые библиотеки, которые вы можете использовать. Поскольку компоненты без состояния являются просто функциями, в основном это классическая проблема "memoization функции".

  • Refs не поддерживаются: https://github.com/facebook/react/issues/4936

Компонент, который расширяет класс PureComponent VS. Нормальный компонент, расширяющий класс Component:

React используется для PureRenderMixin, который вы могли бы прикрепить к классу, определенному с помощью синтаксиса React.createClass. Смесин просто определял бы shouldComponentUpdate, выполняя неглубокое сравнение между следующими реквизитами и следующим состоянием, чтобы проверить, не изменилось ли что-либо. Если ничего не меняется, тогда нет необходимости выполнять повторную визуализацию.

Если вы хотите использовать синтаксис ES6, вы не можете использовать mixins. Поэтому для удобства React представил класс PureComponent, который вы можете наследовать вместо использования Component. PureComponent просто реализует shouldComponentUpdate тем же способом PureRendererMixin. Это в основном удобная вещь, поэтому вам не нужно ее реализовывать самостоятельно, так как нечеткое сравнение текущего/следующего состояния и реквизита, вероятно, является наиболее распространенным сценарием, который может дать вам несколько быстрых побед.

Пример:

class UserAvatar extends Component {
    render() {
       return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
    }
} 

Как вы можете видеть, результат зависит от props.imageUrl и props.username. Если в родительском компоненте вы оказываете <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" /> с тем же реквизитом, React будет вызывать render каждый раз, даже если вывод будет точно таким же. Помните, что React реализует dom diffing, поэтому DOM не будет фактически обновляться. Тем не менее, выполнение диверсии дома может быть дорогостоящим, поэтому в этом случае это будет пустой тратой.

Если компонент UserAvatar расширяет PureComponent, то выполняется неглубокое сравнение. И поскольку реквизиты и nextProps одинаковы, render вообще не будет вызываться.

Примечания к определению "чистый" в реактиве:

В общем, "чистая функция" - это функция, которая всегда оценивает один и тот же результат при одном и том же входе. Результат (для React, то, что возвращается методом render) не зависит от какой-либо истории/состояния и не имеет никаких побочных эффектов (операции, которые изменяют "мир" вне функции).

В React, компоненты без состояния не обязательно являются чистыми компонентами в соответствии с вышеприведенным определением, если вы вызываете "без состояния" компонент, который никогда не вызывает this.setState и который не использует this.state.

Фактически, в PureComponent вы все равно можете выполнять побочные эффекты во время жизненного цикла. Например, вы можете отправить запрос ajax внутри componentDidMount, или вы могли бы выполнить некоторый расчет DOM для динамической настройки высоты div внутри render.

Определение "немых компонентов" имеет более "практическое" значение (по крайней мере, в моем понимании): немой компонент "получает ответ", что делать с помощью родительского компонента через реквизит, и не знает, как это делать но вместо этого использует обратные вызовы реквизита.

Пример "умного" AvatarComponent:

class AvatarComponent extends Component {
    expandAvatar () {
        this.setState({ loading: true });
        sendAjaxRequest(...).then(() => {
            this.setState({ loading: false });
        });
    }        

    render () {
        <div onClick={this.onExpandAvatar}>
            <img src={this.props.username} />
        </div>
    }
}

Пример "немой" AvatarComponent:

class AvatarComponent extends Component {
    render () {
        <div onClick={this.props.onExpandAvatar}>
            {this.props.loading && <div className="spinner" />}
            <img src={this.props.username} />
        </div>
    }
}

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

  • 1
    Я очень ценю ваш ответ и знания, которыми вы поделились. Но мой настоящий вопрос: когда мы должны выбирать что? , Для того же примера, который вы упомянули в своем ответе, как мне его определить? Должен ли он быть функциональным компонентом без сохранения состояния (если так, почему?), Или расширением PureComponent (почему?), Или расширением класса Component (опять же, почему?). Как вы решаете, как вы выбираете между этими тремя в зависимости от назначения / размера / реквизита / поведения наших компонентов?
  • 1
    Нет проблем. Для функционального компонента без сохранения состояния есть список плюсов / минусов, который я могу рассмотреть, чтобы решить, будет ли он подходящим. Это отвечает на первый вопрос? Я попытаюсь ответить на вопрос о выборе немного больше.
Показать ещё 12 комментариев
11

Я не гений, но реагирую, но из моего понимания мы можем использовать каждый компонент в следующих ситуациях

  • Элемент без состояния - - это компонент, который не имеет жизненного цикла, поэтому эти компоненты должны использоваться в рендеринге повторяющегося элемента родительского компонента, например, при отображении текстового списка, который просто отображает информацию и не выполняет никаких действий.

  • Чистый компонент - - это элементы, имеющие жизненный цикл, и они всегда будут возвращать тот же результат, когда задано конкретное состояние. Эти компоненты могут использоваться при отображении списка результатов или конкретных данных объекта, которые не имеют сложных дочерних элементов и используются для выполнения операций, которые влияют только на самих себя. такой список отображения пользовательских карточек или список карточек продуктов (основная информация о продукте) и только пользователь действий может выполнять щелчок, чтобы просмотреть страницу с подробными сведениями или добавить в корзину.

  • Нормальные компоненты или сложные компоненты.. Я использовал термин сложный компонент, потому что они обычно являются компонентами уровня страницы и состоят из множества компонентов для детей, и поскольку каждый из них может вести себя в своем собственном уникальном так что вы не можете быть на 100% уверены, что он даст тот же результат в заданном состоянии. Как я сказал, обычно они должны использоваться в качестве компонентов контейнера.

  • 1
    Этот подход может сработать, но вы можете упустить большой прирост производительности. Использование PureComponent в компонентах корневого уровня и компонентах, находящихся в верхней части иерархии, как правило, PureComponent вам наибольший прирост производительности. Конечно, вам нужно избегать изменяющихся подпорок и состояния напрямую, чтобы чистые компоненты работали правильно, но изменение объектов напрямую в любом случае является препятствием в React.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню