Пишите более чистые компоненты и забудьте о React.FC раз и навсегда

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

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

TL;DR

Вот краткое изложение этой статьи, если вы спешите:

  1. Вы можете воспользоваться выводом типов TypeScript, чтобы избежать явного определения типов компонентов, что означает меньшее количество и более чистый код.
  2. React.FC не следует использовать при определении компонентов в кодовых базах с функциональными компонентами и TypeScript, потому что он:
    — имеет ненужные (при использовании TypeScript) и устаревшие свойства, такие как propTypes, contextTypes и defaultProps.
    — ограничивает вас. для использования функциональных выражений.
    — Не поддерживает дженерики.
    — Усложняет типы компонентов с пространством имен.
  3. Более чистые типы компонентов делают ваш код более перспективным и несвязанным.

1. Пусть TypeScript сделает всю работу 💪

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

Применительно к компонентам React, возможно, лучше позволить TypeScript вывести тип возвращаемого компонента, чем явно определить его, как показано в следующем фрагменте кода.

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

💡 Совет. Включите или отключите следующее правило eslint, чтобы принудительно добавлять явный тип возвращаемого значения или нет: typescript-eslint/explicit-function-return-type

Краткий обзор истории перед следующей темой. С 2013 по 2019 год компоненты React были написаны с использованием классов, которые расширяли бы другие встроенные компоненты, такие как React.Component и React.PureComponent, для создания корректного компонента. Но после выпуска хуков в версии 16.8 мы перешли к написанию компонентов как функций, что намного проще, потому что вы пишете меньше, а все остается на месте.

Функциональные компоненты довольно просты, поскольку они не требуют ничего, чтобы считаться компонентом, кроме возврата допустимых значений компонента, например JSX. Тем не менее, многие люди все еще чувствуют необходимость пометить свои функции как компоненты. Это часто делается с помощью типа React.FC из пакета @types/react. Суть этого типа в том, что он привносит старые и ненужные свойства,некоторые из них из основанного на классах React, в ваши в остальном чистые функциональные компоненты. Как решить эту проблему?

2. Прекратите использовать React.FC

Такие типы, как FunctionComponent/FC и VoidFunctionComponent/VFC в React, были введены, чтобы упростить создание компонентов-функций, сохраняя при этом некоторые полезные свойства, такие как children, и другие внутренние свойства, такие как propTypes, contextTypes, defaultProps и displayName.

1.1. Но нужны ли нам все эти свойства? 🤔

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

Выше мы видим, что он определяет пару свойств:

1️⃣ (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null — это первое свойство определяет параметры и возвращаемый тип самого функционального компонента. Давайте посмотрим на это поближе:

— Параметр props функции соответствует свойствам, которые она принимает (определены пользователем) плюс неявное свойство children. Это заблуждение, так как многие компоненты не принимают children. Если вы ничего не делаете с этим свойством, начиная с @types/react v16.9.48 , вместо этого вы можете использовать тип VoidFunctionComponent/VFC, который делает то же самое, но не добавляет его к вашему типу.

— Параметр context используется для передачи контекста компонентам-потомкам. При использовании хуков это свойство не нужно, так как мы можем и должны прибегать к хуку useContext для использования контекста.

— Тип этого свойства (правая часть двоеточия) определяет, что компонент может возвращать только ReactElement или null, что предотвращает возврат недопустимых значений, таких как undefined.

2️⃣ propTypes — позволяет назначать типы данных свойствам компонента, чтобы иметь возможность добавлять проверку типов к компонентам JavaScript. Как и следовало ожидать, это бесполезно при использовании TypeScript, поскольку мы уже можем определять наши типы, которые он также принимает в качестве параметра: FC<MyType>.

3️⃣ contextTypes — имеет ту же функцию, что и propTypes, но применяется к context, переданному компоненту (упомянутому выше в первом свойстве). Это использовалось в классах JavaScript с устаревшим контекстом.

4️⃣ defaultProps — позволяет назначать значение по умолчанию свойствам, принимаемым компонентом. Полезно для классов, но при использовании функциональных компонентов вместо этого мы можем использовать параметры ES6 по умолчанию:

5️⃣ displayName — позволяет пользователю присвоить компоненту явное имя, которое будет использоваться в сообщениях отладки. По умолчанию имя выводится из имени, функции или класса, определяющего компонент.

1.2. Ограничения React.FC 👎

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

A. Заставляет вас вводить функцию, а не реквизит

При использовании FC вам нужно ввести функцию, а не ее реквизиты, что вынуждает вас использовать функциональные выражения, поскольку объявления функций не могут быть типизированы.

Вот что я имею в виду:

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

B. Не поддерживает дженерики

Если вы хотите написать функциональный компонент с универсальными типами, FC вам не подойдет, так как для этого нет допустимого синтаксиса.

Например, рассмотрим следующий компонент:

При использовании FC, где мы определяем общий?

Это вызывает несоответствия в определениях ваших компонентов, поскольку некоторые из них будут использовать React.FC, а те, которые принимают универсальные типы, — нет.

C. Требуется больше кода для создания компонента с пространством имен

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

Но написание приведенного выше кода с FC требует от вас явного определения всех ваших типов в компоненте пространства имен, что увеличивает количество шаблонов.

Это не главный недостаток, а скорее небольшая жертва простоты.

1.3. Какие есть альтернативы React.FC?

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

Ниже приведены некоторые альтернативы, которые позволяют вам иметь некоторые свойства, предлагаемые FC.

А. дети

Если наш компонент принимает children, для этого есть несколько способов ввести его:

1️⃣ Использование вспомогательных типов from@types/react

Этот пакет позволяет вам добавить это свойство к вашему пользовательскому типу с помощью утилиты PropsWithChildren.

Единственная проблема с этим типом заключается в том, что он требует аргумента, поэтому, если у вас есть компонент, которому требуется только children в качестве реквизита, он не позволяет вам делать следующее: (props: PropsWithChildren)

2️⃣ Определение свойства в вашем типе

Всегда есть возможность определить свойство в вашем пользовательском типе.

3️⃣ Определите собственный тип утилиты

Не хотите печатать каждый раз? Это нормально, я тоже ленивый.

Б. отображаемое имя

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

Нет необходимости определять свойство в типе вашего компонента, так как TypeScript сам выведет его 🚀

C. А как насчет других свойств React.FC?

Я уверен, что они вам не нужны. В настоящее время, если вы используете функциональные компоненты с TypeScript в своей кодовой базе, если вы не используете какой-либо обходной путь, вам не понадобятся такие свойства, как propTypes, contextTypes или defaultProps. Если вы это сделаете, не стесняйтесь оставлять комментарии с вашим вариантом использования, я всегда открыт для изучения и обсуждения этой темы.

1.5. Есть ли польза от использования React.FC?

Такие типы, как FunctionComponent/FC и VoidFunctionComponent/VFC сами по себе не имеют ничего плохого. Если вы безразличны к их ограничениям, они могут быть великолепны в следующих сценариях.

  1. Начинающие знакомятся с типизированным React
  2. Кодовые базы JavaScript
  3. Устаревшие кодовые базы, в которых используются компоненты на основе классов или устаревший контекстный код.

Почему это важно?

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

  • 🚀 Повышает удобство для разработчиков и читабельность кода.
  • 🧼 Продвигает практику чистого кода и использование встроенных функций JavaScript и TypeScript.
  • 💪 Укрепляет знания разработчиков о внутренних механизмах TypeScript и React. Довольно много людей не знают о причинах использования React.FC.
  • 🧩 Делает ваш код более ориентированным на будущее и несвязанным. Это означает, что если по какой-либо причине встроенный тип изменится, у вас будет меньше шансов, что это затронет вас. Кроме того, если вы хотите перейти на другую альтернативу на основе React (например, preact), процесс будет проще, так как вы не привязаны к пакету @types/react (подробнее об этом в этой отличной статье ).

Тем не менее, это не только мое личное мнение, так как еще в начале 2020 года React.FC удалили из официального приложения create-react-app по ряду причин, упомянутых выше.

Если вы хотите удалить все экземпляры React.FC из своей кодовой базы, вы можете использовать этот код jscodeshift (Совет: используйте npx).

📚 Также есть отличные ресурсы и статьи, посвященные этому, на которые я опирался. Обязательно прочитайте их:

Спасибо за прочтение, я надеюсь, что вы узнали что-то из этой статьи, я знаю, что я сделал 🚀

Если вам нравится работать над крупномасштабными проектами с глобальным влиянием и если вам нравятся настоящие вызовы, не стесняйтесь обращаться к нам на xgeeks! Мы расширяем нашу команду, и вы можете стать следующим, кто присоединится к этой группе талантливых людей 😉

Загляните на наши каналы в социальных сетях, если хотите узнать, что происходит в xgeeks! До скорой встречи!