Создание многократно используемых компонентов React с помощью Tailwind
Опубликовано: 2022-03-10В этом посте мы рассмотрим несколько различных способов создания повторно используемых компонентов React, которые используют Tailwind под капотом, предоставляя приятный интерфейс другим компонентам. Это улучшит ваш код, перейдя от длинных списков имен классов к семантическим реквизитам, которые легче читать и поддерживать.
Вам нужно будет работать с React, чтобы хорошо понять этот пост.
Tailwind — это очень популярный CSS-фреймворк, предоставляющий низкоуровневые служебные классы, помогающие разработчикам создавать собственные дизайны. За последние несколько лет его популярность возросла, потому что он действительно хорошо решает две проблемы:
- Tailwind позволяет легко вносить итеративные изменения в HTML, не копаясь в таблицах стилей в поисках подходящих селекторов CSS.
- Tailwind имеет разумные соглашения и значения по умолчанию. Это позволяет людям легко начать работу без написания CSS с нуля.
Добавьте исчерпывающую документацию, и неудивительно, почему Tailwind так популярен.
Эти методы помогут вам преобразовать код, который выглядит следующим образом:
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"> Enable </button>
Чтобы код выглядел так:
<Button size="sm" textColor="white" bgColor="blue-500"> Enable </Button>
Разница между обоими фрагментами заключается в том, что в первом мы использовали стандартный HTML-тег кнопки, а во втором — компонент <Button>
. Компонент <Button>
был создан для повторного использования, и его легче читать, поскольку он имеет лучшую семантику. Вместо длинного списка имен классов он использует свойства для установки различных атрибутов, таких как size
, textColor
и bgColor
.
Давайте начнем.
Метод 1: управление классами с помощью модуля Classnames
Простой способ адаптировать Tailwind к приложению React — использовать имена классов и переключать их программно.
Модуль classnames npm позволяет легко переключать классы в React. Чтобы продемонстрировать, как вы можете это использовать, давайте рассмотрим пример использования, когда у вас есть компоненты <Button>
в вашем приложении React.
// This could be hard to read. <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Enable</button> // This is more conventional React. <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>
Давайте посмотрим, как разделить классы Tailwind, чтобы люди, использующие этот компонент <Button>
, могли использовать реквизиты React, такие как size
, textColor
и bgColor
.
- Передайте реквизиты, такие как
bgColor
иtextColor
, непосредственно в шаблон строки имени класса. - Используйте объекты для программного переключения имен классов (как мы сделали с опорой
size
).
В приведенном ниже примере кода мы рассмотрим оба подхода.
// Button.jsx import classnames from 'classnames'; function Button ({size, bgColor, textColor, children}) { return ( <button className={classnames("bg-${bgColor} text-${textColor} font-bold py-2 px-4 rounded", { "text-xs": size === 'sm' "text-xl": size === 'lg', })}> {children} </button> ) }; export default Button;
В приведенном выше коде мы определяем компонент Button
, который принимает следующие реквизиты:
-
size
Определяет размер кнопки и применяет классы Tailwindtext-xs
илиtext-xl
-
bgColor
Определяет цвет фона кнопки и применяет классы Tailwindbg-*
. -
textColor
Определяет цвет текста кнопки и применяетtext-* classes
. -
children
Здесь будут проходить любые подкомпоненты. Обычно он будет содержать текст внутри<Button>
.
Определив Button.jsx
, мы теперь можем импортировать его и использовать свойства React вместо имен классов. Это упрощает чтение и повторное использование нашего кода.
import Button from './Button'; <Button size="sm" textColor="white" bgColor="blue-500">Enable</Button>
Использование имен классов для интерактивных компонентов
Кнопка — это очень простой вариант использования. А что насчет чего-то более сложного? Что ж, вы можете пойти дальше и создать интерактивные компоненты.
Например, давайте посмотрим на раскрывающийся список, созданный с помощью Tailwind.
Интерактивный раскрывающийся список, созданный с использованием Tailwind и переключения имен классов.
В этом примере мы создаем компонент HTML, используя имена классов Tailwind CSS, но мы предоставляем компонент React, который выглядит следующим образом:
<Dropdown options={\["Edit", "Duplicate", "Archive", "Move", "Delete"\]} onOptionSelect={(option) => { console.log("Selected Option", option)} } />
Глядя на приведенный выше код, вы заметите, что у нас нет классов Tailwind. Все они скрыты внутри кода реализации <Dropdown/>
. Пользователь этого Dropdown
компонента просто должен предоставить список options
и обработчик кликов, onOptionSelect
при option
.
Давайте посмотрим, как этот компонент можно построить с помощью Tailwind.
Удаление части несвязанного кода, вот суть логики. Вы можете просмотреть этот Codepen для полного примера.
import classNames from 'classnames'; function Dropdown({ options, onOptionSelect }) { // Keep track of whether the dropdown is open or not. const [isActive, setActive] = useState(false); const buttonClasses = `inline-flex justify-center w-full rounded-md border border-gray-300 px-4 py-2 bg-white text-sm leading-5 font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-blue-500 active:text-gray-200 transition ease-in-out duration-150`; return ( // Toggle the dropdown if the button is clicked <button onClick={() => setActive(!isActive)} className={buttonClasses}> Options </button> // Use the classnames module to toggle the Tailwind .block and .hidden classes <div class={classNames("origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg", { block: isActive, hidden: !isActive })}> // List items are rendered here. {options.map((option) => <div key={option} onClick={(e) => onOptionSelect(option)}>{option}</div>)} </div> ) } export default Dropdown;
Выпадающий список становится интерактивным, выборочно показывая или скрывая его с помощью классов .hidden
и .block
. Всякий раз, когда <button>
нажата, мы запускаем обработчик onClick
, который переключает состояние isActive
. Если кнопка активна ( isActive === true
), мы устанавливаем класс block
. В противном случае мы устанавливаем hidden
класс. Это оба класса Tailwind для переключения поведения отображения.
Таким образом, модуль classnames — это простой и эффективный способ программного управления именами классов для Tailwind. Это упрощает разделение логики на реквизиты React, что упрощает повторное использование ваших компонентов. Это работает для простых и интерактивных компонентов.
Метод 2: использование констант для определения системы дизайна
Другой способ совместного использования Tailwind и React — использование констант и сопоставление свойств с определенной константой. Это эффективно для систем проектирования зданий. Продемонстрируем на примере.
Начните с файла theme.js
, в котором вы перечисляете свою дизайн-систему.
// theme.js (you can call it whatever you want) export const ButtonType = { primary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", secondary: "bg-blue-500 hover:bg-blue-700 text-white font-bold rounded", basic: "bg-white hover:bg-gray-700 text-gray-700 font-bold rounded", delete: "bg-red-300 hover:bg-red-500 text-white font-bold rounded" }; export const ButtonSize = { sm: "py-2 px-4 text-xs", lg: "py-3 px-6 text-lg" }
В этом случае у нас есть два набора констант:
-
ButtonType
определяет стиль кнопок в нашем приложении. -
ButtonSizes
определяет размеры кнопок в нашем приложении.
Теперь давайте напишем наш компонент <Button>
:
import {ButtonType, ButtonSize} from './theme'; function Button({size, type, children}) { // This can be improved. I'm keeping it simple here by joining two strings. const classNames = ButtonType[type] + " " + ButtonSize[size]; return ( <button className={classNames}>{children}</button> ) } export default Button;
Мы используем ButtonType
и ButtonSize
для создания списка имен классов. Это делает интерфейс нашей <Button>
намного приятнее. Это позволяет нам использовать size
и type
вместо того, чтобы помещать все в строку имени класса.
// Cleaner and well defined props. <Button size="xs" type="primary">Enable</Button>
По сравнению с предыдущим подходом:
// Exposing class names <button className="py-2 px-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Enable</button>
Если вам нужно переопределить внешний вид кнопок в вашем приложении, просто отредактируйте файл theme.js
, и все кнопки в вашем приложении будут автоматически обновлены. Это может быть проще, чем искать имена классов в различных компонентах.
Способ 3: создание утилит с помощью @apply
Третий способ улучшить удобочитаемость ваших компонентов React — использовать CSS и шаблон @apply
, доступный в PostCSS, для извлечения повторяющихся классов. Этот шаблон предполагает использование таблиц стилей и постпроцессоров.
Давайте продемонстрируем, как это работает, на примере. Предположим, у вас есть группа кнопок, в которой есть основная и дополнительная кнопки.
<button className="py-2 px-4 mr-4 text-xs bg-blue-500 hover:bg-blue-700 text-white font-bold rounded">Update Now</button> <button className="py-2 px-4 text-xs mr-4 hover:bg-gray-100 text-gray-700 border-gray-300 border font-bold rounded">Later</button>
Используя шаблон @apply
, вы можете написать этот HTML-код следующим образом:
<button className="btn btn-primary btn-xs">Update Now</button> <button className="btn btn-secondary btn-xs">Later</button>
Который затем может быть адаптирован к React, чтобы стать:
import classnames from "classnames"; function Button ({size, type, children}) { const bSize = "btn-" + size; const bType = "btn-" + type; return ( <button className={classnames("btn", bSize, bType)}>{children}</button> ) } Button.propTypes = { size: PropTypes.oneOf(['xs, xl']), type: PropTypes.oneOf(['primary', 'secondary']) }; // Using the Button component. <Button type="primary" size="xs">Update Now</Button> <Button type="secondary" size="xs">Later</Button>
Вот как вы могли бы создать эти имена классов в стиле БЭМ, такие как .btn
, .btn-primary
и другие. Начните с создания файла button.css
:
/\* button.css \*/ @tailwind base; @tailwind components; .btn { @apply py-2 px-4 mr-4 font-bold rounded; } .btn-primary { @apply bg-blue-500 hover:bg-blue-700 text-white; } .btn-secondary { @apply hover:bg-gray-700 text-gray-700 border-gray-300 border; } .btn-xs { @apply text-xs; } .btn-xl { @apply text-xl; } @tailwind utilities;
Приведенный выше код не является настоящим CSS, но он будет скомпилирован PostCSS. Здесь доступен репозиторий GitHub, в котором показано, как настроить PostCSS и Tailwind для проекта JavaScript.
Здесь также есть короткое видео, демонстрирующее, как его настроить.
Недостатки использования @apply
Концепция извлечения служебных классов Tailwind в CSS-классы более высокого уровня кажется разумной, но у нее есть некоторые недостатки, о которых вам следует знать. Давайте выделим их на другом примере.
Во-первых, извлекая эти имена классов, мы теряем некоторую информацию. Например, нам нужно знать, что .btn-primary
нужно добавить к компоненту, к которому уже применено .btn
. Кроме того, .btn-primary
и .btn-secondary
не могут применяться вместе. Эта информация не очевидна, просто глядя на классы.
Если бы этот компонент был чем-то более сложным, вам также нужно было бы понимать отношения родитель-потомок между классами. В некотором смысле, это проблема, для решения которой был разработан Tailwind, и с помощью @apply
мы возвращаем проблемы по-новому.
Вот видео, в котором Адам Ватан — создатель Tailwind — подробно рассказывает о плюсах и минусах использования @apply
.
Резюме
В этой статье мы рассмотрели три способа интеграции Tailwind в приложение React для создания повторно используемых компонентов. Эти методы помогают вам создавать компоненты React с более чистым интерфейсом, используя props
.
- Используйте модуль classnames для программного переключения классов.
- Определите файл констант, в котором вы определяете список классов для каждого состояния компонента.
- Используйте
@apply
для извлечения классов CSS более высокого уровня.
Если у вас есть какие-либо вопросы, отправьте мне сообщение в Twitter на @tilomitra.
Рекомендуемое чтение на SmashingMag:
- Настройка Tailwind CSS в проекте React
- Создание сортируемых таблиц с помощью React
- Руководство по новым и экспериментальным CSS DevTools в Firefox
- Создайте свои собственные расширяющиеся и сужающиеся панели содержимого