Поиск по сайту:

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


Компоненты высшего порядка (HOC) — это своего рода компонент React, который помогает вам повторно использовать логику в вашем приложении. Терминология может показаться сложной, но с HOC легко разобраться, и они могут упростить поддержку вашей кодовой базы.

Компонент более высокого порядка всегда включает в себя дочерние компоненты с дополнительными функциями. HOC определяется как функция, которая принимает компонент в качестве параметра. Затем он возвращает новый компонент, который обычно отображает входной компонент, обернутый дополнительными реквизитами.

Простой пример

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

Предположим, что этот объект представляет состояние нашего приложения:

{
    checkout: {
        items: [
            {
                label: "Product 1",
                price: 150.00,
                quantity: 2
            },
            {
                label: "Product 2",
                price: 75.00,
                quantity: 1
            }
        ]
    }
}

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

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

Представляем HOC

Давайте посмотрим, как HOC может помочь:

import React from "react";
import {connect} from "react-redux";
 
const withCheckout = ComponentToWrap => {
 
    const ComponentWithCheckout = class extends React.Component {
 
        render() {
            return (
                <ComponentToWrap
                    checkoutItems={this.props.checkout.items}
                    checkoutTotal={this.total}
                    {...this.props} />
            );
        }
 
        get total() {
            const prices = this.props.checkout.items.map(i => (i.quantity * i.price));
            return prices.reduce((a, b) => (a + b), 0);
        }
 
    }
 
    return connect(({checkout}) => ({checkout}))(ComponentWithCheckout);
 
}
 
export default withCheckout;

Файл экспортирует единственную функцию withCheckout, которая принимает компонент React в качестве единственного параметра (ComponentToWrap). Внутри функции мы создаем новый анонимный класс, который сам является компонентом React.

Метод render этого нового компонента создает экземпляр ComponentToWrap, который мы передали в функцию. Теперь у нас есть возможность определить свойства экземпляра. Мы пересылаем массив элементов оформления заказа как checkoutItems и делаем предварительно вычисленное общее значение доступным как checkoutTotal.

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

Мы используем метод connect в react-redux, поэтому свойство checkout в HOC получает значение checkout. key в состоянии нашего магазина Redux. Это деталь реализации — ваш HOC может поддерживать свое собственное состояние или обращаться к какой-либо другой службе в вашем приложении.

Использование HOC

Теперь пришло время использовать наш HOC.

import React from "react";
import withCheckout from "./withCheckout.js";
 
class CheckoutReviewScreen extends React.Component {
 
    render() {
        return (
            <h1>Checkout</h1>
            <h2>{this.props.checkoutTotal}</h2>
        );
    }
 
}
 
export default withCheckout(CheckoutReviewScreen);

Мы предполагаем, что наш withCheckout HOC сохранен в withCheckout.js в том же каталоге, что и наш новый компонент экрана просмотра проверки. Обернув компонент нашим HOC withCheckout, мы можем получить доступ и отобразить общую стоимость заказа. Нам не нужно вычислять его самостоятельно или хранить в состоянии приложения. Если мы когда-нибудь захотим обновить способ расчета общей суммы (например, добавить фиксированную плату за обработку), нам нужно будет внести изменения только в одном месте — в нашем HOC.

Теперь вы можете визуализировать

<CheckoutReviewScreen />

в любом месте вашего приложения. В наш обернутый пример не нужно передавать какие-либо реквизиты, поскольку он получает данные из нашего хранилища Redux. Поскольку он обернут withCheckout, который сам обернут connect Redux, экран обзора автоматически получает реквизит checkoutTotal, который суммирует цены всех предметов в состояние приложения.

Теперь стоит упомянуть, как мы назвали наш HOC: withCheckout. По соглашению имена HOC обычно имеют префикс with, потому что они добавляют что-то к компонентам, которые они оборачивают. В нашем случае HOC обеспечивает удобный доступ к нашей корзине оформления заказа, которую в противном случае нам пришлось бы реализовывать в каждом компоненте.

Преимущества HOC

Использование HOC позволяет абстрагировать общее поведение от компонентов, сводя к минимуму дублирование кода и повышая удобство сопровождения. HOC позволяют вводить зависимости. Они помогают вам упростить ваши компоненты, позволяя передавать больше из внешнего мира.

HOC распространены в экосистеме React. На самом деле, в этой статье мы видели один — connect(), часть react-redux, который подписывается на изменения состояния ваших компонентов в Redux.

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

Вы можете передать любой компонент своего приложения в withCheckout, ничего не нарушая — HOC просто добавляет несколько дополнительных реквизитов. Вот почему так важно, чтобы ваши HOC пересылали все реквизиты, которые они получали ({...this.props} в нашем примере). Они не должны делать ничего, что могло бы помешать нормальной работе обернутого компонента.

Может показаться, что ваши компоненты теперь зависят от вашего HOC. Это не так. Вы можете экспортировать вторую версию вашего компонента без упаковки, предоставляя потребителям выбор того, что они будут использовать.

Ваш компонент на самом деле настаивает только на получении определенных реквизитов — checkoutTotal в нашем случае. Это может быть предоставлено HOC или путем передачи значения везде, где компонент отображается. Наш HOC упрощает разработку, но принципиально не меняет природу наших визуализируемых компонентов.




Все права защищены. © Linux-Console.net • 2019-2024