React.js — суперзвезда разработки интерфейса JavaScript! Это незаменимая библиотека для создания привлекательных пользовательских интерфейсов, и разработчики и организации по всему миру не могут ею насытиться. Благодаря отличной компонентной архитектуре и великолепному декларативному подходу к рендерингу React.js предоставляет мощную и гибкую платформу для создания сложных и масштабируемых веб-приложений.

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

Существует множество шаблонов проектирования на выбор, но некоторые из самых популярных для React.js включают:

  • Шаблон компонента высшего порядка (HOC)
  • Рендеринг реквизита шаблона
  • Редукс и шаблоны контекста
  • Крючки
  • Границы ошибки
  • Шаблон контейнера/презентатора
  • Шаблон составных компонентов.

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

Итак, если вам наскучило «Hello World!» примеры, которые вы найдете повсюду в Интернете, пристегните ремень безопасности и приготовьтесь увидеть иллюстрации из реального мира.

Давайте начнем!

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

Если вы ранее видели или использовали withRouter из пакета react-router или Connect(mapStateToProps, MapDispatchToProps) (UserPage)при использовании react-redux, то вы видели HOC в действии 🔥.

Шаблон HOCs позволяет вам повторно использовать код, оборачивая один компонент другим. HOC используется для добавления функциональности к существующему компоненту без изменения его кода, «например, передача истории маршрутизатора и местоположения компонента в компонент или подключение компонентов к хранилищу избыточности».

Хватит красивых слов, давайте рассмотрим пример создания простого HOC, прежде чем показывать некоторые продвинутые реализации HOC в реальном мире:

import React from 'react';

// Define the HOC function
const withColor = (WrappedComponent, color) => {
  const ComponentWithColor = (props) => {
    // Add the color prop to the wrapped component
    return <WrappedComponent {...props} color={color} />;
  };
  
  return ComponentWithColor;
};

// Define a component that will be wrapped
const MyComponent = { color } => {
  return <div style={{ backgroundColor: props.color }}>Hello, World!</div>;
};

// Use the HOC to create a new component
const ColoredComponent = withColor(MyComponent, 'red');

// Render the new component
const App = () => {
  return <ColoredComponent />;
};

В этом примере мы определили функцию HOC с именем withColor, которая принимает WrappedComponent и color в качестве аргументов. Функция возвращает новый компонент, который добавляет реквизит color к WrappedComponent.

Затем мы определили простой компонент под названием MyComponent, который будет обернут HOC. Компонент просто отображает div с фоновым цветом на основе свойства color.

Затем мы использовали функцию HOC для создания нового компонента с именем ColoredComponent, передав MyComponent и цвет 'red' в качестве аргументов. Наконец, мы визуализировали новый компонент в компоненте App.

С помощью этого HOC мы можем легко создавать новые компоненты с разными цветами фона без необходимости дублировать код для MyComponent. Мы можем просто использовать функцию withColor для создания нового компонента нужного цвета.

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

ВНИМАНИЕ🚨, ВПЕРЕДИ РЕАЛЬНЫЙ СЦЕНАРИЙ!

Вот пример HOC, который добавляет аутентификацию к компоненту:

import React from 'react';
import { useHistory } from 'react-router-dom';

// Define the HOC function
const withAuthentication = (WrappedComponent) => {
  const ComponentWithAuthentication = (props) => {
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const history = useHistory();

    useEffect(() => {
      const isAuthenticated = checkIfAuthenticated(); // A function that checks if the user is authenticated
      if (isAuthenticated) {
        setIsAuthenticated(true);
      } else {
        history.push('/login'); // Redirect the user to the login page
      }
    }, []);

    // Render the wrapped component if the user is authenticated
    // Render a message if the user is not authenticated
    if (isAuthenticated) {
      return <WrappedComponent {...props} />;
    } else {
      return <div>Please log in to access this page</div>;
    }
  };

  return ComponentWithAuthentication;
};

// Define a component that will be wrapped
const MyComponent = (props) => {
  return <div>Hello, World!</div>;
};

// Use the HOC to create a new component
const ComponentWithAuth = withAuthentication(MyComponent);

// Render the new component
const App = () => {
  return <ComponentWithAuth />;
};

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

Затем мы определили простой компонент с именем MyComponent, который будет заключен в HOC. Компонент просто отображает div с текстом Hello, World!.

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

ЕЩЕ ОДИН ПРИМЕР ИЗ РЕАЛЬНОГО МИРА!

Точно так же мы также можем создать HOC, который добавляет выборку данных в компонент. Вот пример:

import React from 'react';

// Define the HOC function
const withDataFetching = (url) => (WrappedComponent) => {
  // Define the WithDataFetching component
  const WithDataFetching = (props) => {
    // Declare state variables
    const [data, setData] = useState(null);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState(null);

    // Fetch data from the provided URL and update state with the response
    useEffect(() => {
      // notice how we define async functions in a useEffect hook, more on that in an upcoming article
      const fetchData = async () => {
        setIsLoading(true);
        try {
          const response = await fetch(url); // Fetch data from the provided URL
          const data = await response.json(); // Extract data from the response
          setData(data);
          setIsLoading(false);
        } catch (error) {
          setError(error);
          setIsLoading(false);
        }
      };
      fetchData();
    }, [url]);

    // Render the wrapped component with the data and isLoading state as props
    return (
      <WrappedComponent
        data={data}
        isLoading={isLoading}
        error={error}
        {...props}
      />
    );
  };

  // Return the WithDataFetching component
  return WithDataFetching;
};

// Define the component that will be wrapped
const MyComponent = (props) => {
  return (
    <div>
      {props.isLoading && <div>Loading...</div>}
      {props.data && <div>Data: {props.data}</div>}
      {props.error && <div>Error: {props.error}</div>}
    </div>
  );
};

// Use the HOC to create a new component
const ComponentWithDataFetching = withDataFetching(
  'https://jsonplaceholder.typicode.com/posts/1'
)(MyComponent);

// Render the new component
const App = () => {
  return <ComponentWithDataFetching />;
};

В этом примере мы определили функцию HOC с именем withDataFetching, которая принимает URL-адрес в качестве аргумента и возвращает новую функцию с именем WithDataFetching, которая принимает WrappedComponent в качестве аргумента. Функция возвращает новый компонент, который извлекает данные из предоставленного URL-адреса и передает данные, состояние загрузки и состояние ошибки в качестве свойств обернутому компоненту.

Затем мы определили компонент под названием MyComponent, который будет обернут нашим HOC, добавляющим функциональность выборки данных. Компонент принимает три реквизита: isLoading, data и error.

Затем мы использовали функцию withDataFetching для создания нового компонента с именем ComponentWithDataFetching, передав MyComponent в качестве аргумента и URL-адрес для получения данных. Новый компонент будет отображать MyComponent с данными, состоянием загрузки и состоянием ошибки в качестве реквизита.

Наконец, мы визуализировали новый компонент в компоненте App. Когда компонент монтируется, он извлекает данные из предоставленного URL-адреса и передает полученные данные, состояние загрузки и состояние ошибки в качестве реквизита в MyComponent.

Преимущества и недостатки использования HOC:

Хотя HOC предлагают несколько преимуществ, они также имеют некоторые недостатки. Вот сравнение преимуществ и недостатков использования HOC в React:

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

  • Возможность повторного использования кода: HOC позволяют повторно использовать код, извлекая общие функции из нескольких компонентов в один HOC. Это может уменьшить дублирование кода и сделать ваш код более удобным для сопровождения.
  • Разделение проблем: HOC позволяют разделить задачи между компонентами, изолируя определенные функции внутри HOC. Это может сделать ваш код более модульным и простым в обслуживании.
  • Состав: HOC позволяют составлять компоненты гибким и расширяемым способом. Оборачивая компоненты в HOC, вы можете создавать сложные и изощренные пользовательские интерфейсы без необходимости писать много шаблонного кода.
  • Гибкость: HOC предлагают большую гибкость, поскольку их можно использовать с любым компонентом, независимо от его реализации. Это упрощает применение общих функций к нескольким компонентам без изменения их реализации.

Недостатки:

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

Заключение:

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

Но остерегайтесь недостатков, которые они приносят:
Детализация свойств, конфликты имен — это вещи.
Кроме того, сложность и накладные расходы на производительность,
Инверсия наследования может вас застать.

Тщательно обдумайте свои потребности и требования,
прежде чем решить, действительно ли HOC справедливы.
взвесьте все «за» и «против», и тогда вы поймете,
являются ли HOC правильным выбором для вас.

Если вы достигли этого момента «и вам понравилось рифмованное заключение 😬», рассмотрите возможность поаплодировать 👏 и подписаться на мой аккаунт, чтобы в будущем получать больше подобного контента.