Создавайте наборы тем и внедряйте их как один объект в провайдеры тем React.

Вступление

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

Я реализовал это решение на целевой странице веб-сайта моей компании, в которую уже были встроены светлые и темные темы до внедрения.

Я стремился представить темы на основе криптовалюты, не нарушая и не реорганизуя темы по умолчанию. Решенная проблема заключалась в том, чтобы не смешивать темы на основе криптовалюты (биткойн, Ethereum и DOT) со светлой и темной темами по умолчанию.

Другими словами, темы Crypto должны существовать в отдельном файле по сравнению с темами по умолчанию.

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

Нет ограничений на количество «файлов тем», которые вы вводите в свое приложение.

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

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

Реагировать на интуицию темы: предварительное чтение

Контекст темы, используемый здесь, обеспечивается компонентом Стилизованные компоненты ThemeProvider, а значения темы, которые будут импортированы в ваши компоненты, зависят от объектов Стилизованная тема ThemeSet.

Чтобы узнать больше о том, как интегрировать стилизованную тематику и стилизованные компоненты в ваши проекты React, обратитесь к моей опубликованной статье: React Dark Mode со стилизованной темой и контекстом.

Если вы разрабатываете приложение React Native, описанная здесь техника глубокого слияния также может быть применена к таким проектам. Чтобы понять, как тематика применяется конкретно в React Native, обратитесь к моей опубликованной статье: React Dark Mode со стилизованными темами и контекстом.

Наш подход к использованию нескольких файлов тем в этой статье основан на этих основах тематики.

Решение: общий обзор

Это решение можно рассматривать как трехэтапный процесс.

  • Шаг 1. Определите конфигурации вашей темы в отдельных файлах.
  • Шаг 2: Импортируйте все конфигурации темы и объедините их в один объект.
  • Шаг 3: Импортируйте объединенный объект и распространите свойства в объектах ThemeSet. Именно эти объекты импортируются в ваши компоненты для определения значения на основе текущей активной темы.

Для общей интуиции на следующем снимке экрана показаны 4 файла в этой трехэтапной реализации:

Эти файлы будут рассмотрены более подробно ниже, но полезно знать, как они работают вместе, чтобы понять решение на высоком уровне:

  • Первые два файла, default.ts и crypto.ts, определяют темы по умолчанию (light и dark) и темы Crypto (btc, eth и dot) соответственно. Первый файл состоит из двух тем, а криптофайл состоит из трех. Обратите внимание, что количество тем для каждого файла не имеет значения - все они будут объединены вместе.

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

  • Третий файл, all.ts, импортирует обе конфигурации темы и объединяет их в единый объект с именем AllThemes. Для проведения глубокого слияния здесь использовалась утилита lodash.merge. Это этап агрегирования, который объединяет все конфигурации вашей темы в один объект.
  • Последний файл, theme.ts, импортирует объект AllThemes и добавляет свойства темы к объектам ThemeSet, которые предоставляются пакетом styled-theming. Этот недооцененный пакет позволяет вам импортировать значения темы (например, цвет фона заголовка) в ваш ThemeProvider компонент и автоматически предоставлять правильное значение в зависимости от текущей активной темы.

Итак, первые 2 файла определяют цвета темы для каждого атрибута, который вы хотите тематизировать - и нет ограничений на количество этих файлов, которые вы определяете. Файл 3 объединяет все эти файлы в один объект AllThemes. И, наконец, AllThemes импортируется и используется в файле 4 для создания ThemeSet объектов, которые используются в ваших компонентах пользовательского интерфейса.

Как строятся наборы тем

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

theme.ts определяет каждый набор тем. Возьмем backgroundColor:

export const backgroundColor: theme.ThemeSet = theme('mode', {
   ...AllThemes.background.primary,
});

Здесь мы предоставляем это ThemeSet каждому свойству в нашем AllThemes.background.primary объекте. Фактически это соответствует каждому свойству background.primary, определенному в наших файлах конфигурации темы.

Вот буквальное значение:

export const backgroundColor: theme.ThemeSet = theme('mode', {
   light: '#fafafa',
   dark: '#0e0f11',
   btc: '#fafafa',
   eth: '#fafafa',
   dot: '#fafafa'
});

Теперь давайте обратим наше внимание на то, как на самом деле устроен AllThemes. Вот оператор слияния, определяющий AllThemes:

// all.ts
import merge from 'lodash.merge';
import defaultThemes from './default';
import cryptoThemes from './crypto';
export const AllThemes = merge(
   defaultThemes, 
   cryptoThemes,
   // could merge more themes here...
);
export default AllThemes;

Функция Lodash merge принимает произвольное количество объектов, поэтому вы можете объединить здесь столько файлов тем, просто разделив их запятыми в пределах merge() - очень полезная функция.

Чтобы добавить lodash.merge в свой проект, просто добавьте зависимость слияния, вам не нужно устанавливать весь пакет Lodash:

yarn add lodash.merge

Глубокое слияние объединит все подсвойства объекта с другим объектом без удаления каких-либо глубоких свойств в процессе.

Глубокое слияние резко контрастирует с мелким слиянием, которое обычно относится к перезаписи свойств после одного дочернего свойства. Это означает, что более глубокие свойства, существовавшие до неглубокого слияния, будут удалены, если они не существуют для нового слитого объекта.

Буквальный процесс глубокого слияния

Снова обращаясь к свойству background.primary, функция слияния объединит свойства light и dark объекта defaultThemes со свойствами btc, dot и eth объекта cryptoThemes.

Если рассматривать этот процесс буквально, defaultThemes первичный фон…

...
background: {
   primary: {
      light: '#fafafa',
      dark: '#0e0f11',
   }
   ...
}
...

… Объединяется с cryptoThemes основным фоном…

...
background: {
   primary: {
      btc: '#fafafa',
      dot: '#f8f8f8',
      eth: '#fcfcfc',
   }
   ...
}
...

… Что приводит к объединенному AllThemes первичному фону:

...
background: {
   primary: {
      light: '#fafafa',
      dark: '#0e0f11'
      btc: '#fafafa',
      dot: '#f8f8f8',
      eth: '#fcfcfc',
   }
   ...
}
...

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

Определение тем

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

На следующем снимке экрана показан файл default.ts в его полной форме. Объект defaultThemes определяет общую структуру самой темы. Обратите внимание на функцию v(), определенную вверху - это будет обсуждаться дальше:

Точно так же файл crypto.ts придерживается той же структуры, за исключением того, что функция v() вводит свойства для 3-х тем вместо 2-х тем:

Обратите внимание, что функция v() позволяет нам определять точно такую ​​же структуру для каждого файла и просто передавать значения v(), чтобы соответствовать количеству параметров, которые принимает функция - или, в нашем случае, количеству определяемых тем. Это дает два преимущества:

  • По подписи v() мы ясно видим, сколько тем необходимо определить вместе с их названиями.
  • Нам просто нужно передать v(…values) в основной объект конфигурации без необходимости определять вспомогательные свойства.

Следовательно, если вы определяете, скажем, 5 тем в конкретном файле, все, что вам нужно изменить, это:

  • Имена параметров v() и его возвращаемых свойств.
  • Количество значений, переданных в v() в объекте конфигурации темы.

Собираем все вместе

Теперь мы подробно рассмотрели реализацию, читатель может взять то, что полезно, и применить это к своей настройке тем.

Что сработало для моей реализации, так это определение папки theme/ в каталоге src/ вашего проекта, в которой хранятся каждая конфигурация темы, ваш all.ts файл агрегирования и ваши ThemeSet определения:

# theme folder structure 
src/
 theme/
    all.ts
    crypto.ts
    default.ts
    theme.ts
 ...

С такой настройкой вы можете просто обернуть свое дерево компонентов <ThemeProvider /> компонентом и импортировать необходимые ThemeSets в свои компоненты.

Если вы создаете новый файл конфигурации темы, просто не забудьте импортировать его в all.ts и добавить в функцию merge(). И это будет все необходимое обслуживание - больше ничего не трогали.

Для получения дополнительных сведений о настройке компонентов вашей темы в соответствии с этой настройкой ознакомьтесь с моей соответствующей статьей: Реагировать на темный режим со стилизованной темой и контекстом.

В итоге

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

Я надеюсь, что это также пролило свет на жизнеспособный вариант использования глубокого слияния в сфере приложений React.