Экосистема внешнего интерфейса развивается чрезвычайно быстро, с появлением таких фреймворков, как React, Vue и Angular, что привело к кардинальным изменениям в архитектуре приложений и появлению совершенно нового набора шаблонов для изучения разработчиками JavaScript.

Когда WordPress использует React в новом редакторе Gutenberg, миллионы разработчиков внезапно попадают в этот мир и пытаются его наверстать.

В этом посте мы разберем один из тех архитектурных паттернов, которые чрезвычайно распространены в React, - компоненты более высокого порядка.

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

edit: withColors( 'backgroundColor', { textColor: 'color' } )(
    function( props ) {
        // Props added by withColors HOC.
        var backgroundColor = props.backgroundColor;
        var setBackgroundColor = props.setBackgroundColor;
        var textColor = props.textColor;
        var setTextColor = props.setTextColor;

        // Class computation
        var paragraphClasses = (
            ( backgroundColor.class || '' ) + '' + ( textColor.class || '' )
        ).trim();

        return el( Fragment, {},
            el( 'p', {
                className: paragraphClasses,
                style: {
                    backgroundColor: backgroundColor.value,
                    color: textColor.value,
                }
            },
                'Hello world'
            ),
            el( InspectorControls, {},
                el( PanelColor, {
                    colorValue: backgroundColor.value,
                    title: __( 'Background Color' ),
                    onChange: setBackgroundColor,
                } ),
                el( PanelColor, {
                    colorValue: textColor.value,
                    title: __( 'Text Color' ),
                    onChange: setTextColor,
                } )
            )
        );
    }
),

Это немного пугает - что именно происходит? Функция withColors реализует шаблон, называемый компонент более высокого порядка. Давайте разберемся, что это значит:

Компоненты высшего порядка

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

Как определено в документации React:

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

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

Эта оболочка представляет собой функцию, которая принимает компонент и возвращает «упакованную» версию этого компонента, которая получит эти функции и данные как props.

Таким образом, вместо каждого компонента, который должен иметь доступ к информации о цвете _ наследование_ от «цветного» компонента или импорт модуля «библиотеки» цветов, который им нужно вызвать, они вместо этого импортируют withColors компонент более высокого порядка (HOC), 'wrap' их компонент с этим компонентом, и теперь их компонент будет получать реквизиты, которые имеют отношение к цвету, вместе с любыми другими реквизитами, которые он специально определяет.

Голая минимальная компонента высшего порядка

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

Минимальный HOC будет просто функцией, обертывающей функцию, например

import secretKeyHOC from 'secret-key';
const BareComponent = function(props) {
  //do something
}
const myComponent = secretKeyHOC(BareComponent);
export default myComponent;

Где secretKeyHOC определен где-то еще и может выглядеть так:

const secretKeyHOC = function(component) {
  return function(props) {
    return component({... props, secretKey: 'mySecretKey'});
  });
}

По сути, HOC просто объединяется с некоторыми новыми реквизитами для вашего компонента - в этом примере с голыми костями он объединяется в секретный ключ.

Компоненты высшего порядка с аргументами

Большинство компонентов более высокого порядка не так просты, как приведенный выше секретный ключ… они инкапсулируют логику многократного использования, но, как правило, их необходимо каким-то образом настроить.

Например, наш компонент secretKey может содержать логику для поиска одного из нескольких секретных ключей, но его необходимо настроить, чтобы знать, какой ключ предоставить для какого компонента.

Для этого компоненты более высокого порядка часто реализуются в виде цепочки функций. Сначала вы вызываете функцию с аргументами конфигурации, которая затем возвращает другую функцию, которую можно использовать для обертывания вашего компонента.

Например, если бы мы могли настроить secretKeyHOC на выбор секретного ключа github или секретного ключа gitlab, это могло бы выглядеть так:

const secretKeyHOC = function(keyType) {
  const keys = {gitlab: 'key1', github: 'key2' }
  const key = keys[keyType];
  return function(component) {
    return function(props) {
      return component({... props, secretKey: key});
    });
  }
}

Затем мы вызываем компонент следующим образом:

import secretKeyHOC from 'secret-key';
const BareComponent = function(props) {
  //do something
}
const myComponent = secretKeyHOC('gitlab')(BareComponent);
export default myComponent;

Сначала мы вызываем функцию, передавая наши аргументы конфигурации, затем вызываем возвращаемую функцию с компонентом, который мы упаковываем.

Вернуться к withColors

Оглядываясь назад на withColors от Гутенберга, мы видим в документации, что HOC принимает следующие аргументы:

withColors HOC может принимать несколько аргументов. Каждый аргумент HOC withColors может быть строкой или объектом. Если аргумент является объектом, он должен содержать только один ключ со значением. Ключ должен быть именем атрибута, в котором установлены предопределенные цвета, например: «textColor». Значение должно соответствовать контексту, в котором используется этот цвет, например: «цвет».

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

edit: withColors( 'backgroundColor', { textColor: 'color' } )(
    function( props ) {
        // Props added by withColors HOC.
        var backgroundColor = props.backgroundColor;
        var setBackgroundColor = props.setBackgroundColor;
        var textColor = props.textColor;
        var setTextColor = props.setTextColor;

        // some more stuff we'll ignore
    }
),

С нашим пониманием компонентов более высокого порядка мы теперь можем точно увидеть, что это делает.

Сначала мы вызываем withColors с парой аргументов, указывающих, что нам нужны backgroundColor и textColor. Это возвращает «функцию-оболочку» (HOC), которую мы вызываем, передавая в наш базовый компонент - функцию, которая будет получать реквизиты.

Это гарантирует, что компонент всегда будет получать 4 свойства: backgroundColor, setBackgroundColor, textColor и setTextColor в дополнение к свойствам, переданным его родителем.

Этот «завернутый» компонент затем назначается edit как компонент, который будет использоваться для редактирования этого блока Гутенберга.

Таким образом, Гутенберг создает чистый, изолированный, основанный на реквизитах подход к модификации и использованию локальных изменений цвета внутри блока.

P.S. - Если вас интересуют такие темы, вы можете подписаться на меня в Twitter или подписаться на мою рассылку новостей. Я отправляю еженедельный информационный бюллетень под названием Friday Frontend. Каждую пятницу я рассылаю 15 ссылок на лучшие статьи, учебные пособия и объявления по CSS / SCSS, JavaScript, а также множество других замечательных новостей Front-end. Зарегистрируйтесь здесь: https://zendev.com/friday-frontend.html

Первоначально опубликовано на zendev.com 24 июля 2018 г.

✉️ Подпишитесь на рассылку еженедельно Email Blast от CodeBurst 🐦 Подпишитесь на CodeBurst на Twitter , просмотрите 🗺️ Дорожная карта веб-разработчиков на 2018 год и 🕸️ Изучите веб-разработку с полным стеком .