Совместное использование кода в приложениях React и React Native

React и React Native помогают сблизить веб- и мобильную разработку, улучшая опыт как пользователей, так и разработчиков.

Некоторые продвинутые команды уже создают полноценную кроссплатформенную систему проектирования с помощью React и React Native (видеоурок).

Аккордеон React-Native, который мы создадим; для веб-приложения у нас будет также аккордеон React

Хотя React и RN требуют различной реализации (браузеры и мобильные устройства не одинаковы), типы приложений и компонентов зачастую имеют общий код, бизнес-логику, хуки, реквизиты и даже дизайн-токены.

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

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

Здесь можно найти (и использовать) все компоненты из этого руководства.

Итак, погрузимся в процесс.

1. Два компонента: React и React Native

Нам предстоит работа с двумя аккордеонами  —  одним для React, другим для React Native.

Вот компонент аккордеона React.

Изучите и установите/откройте его, чтобы протестировать локально или отредактировать.

Другой компонент  —  React Native.

Уделите пару секунд изучению графа “зависимости” каждого компонента. Согласитесь, у них много общих зависимостей.

Общие зависимости компонентов React и React Native

2. Превращение общего кода в общие зависимости

Наша задача  —  сделать так, чтобы каждый аккордеон содержал минимум платформенно-ориентированного кода (поскольку API у них разные), и передать все общие части.

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

Вот зависимости компонентов, которые нужно создать и скомпоновать с аккордеонами React и React Native (можно найти их все здесь):

share-react-react-native              --> scope
├── api --> namespace
│ ├── accordion --> component
│ └── accordion-items --> component
├── design-tokens --> namespace
│ ├── base-tokens --> component
│ ├── react-tokens --> component
│ └── rnative-tokens --> component
├── base-ui --> namespace
│ ├── hooks --> namespace
│ │ ├── use-open --> component
│ │ └── use-select --> component
│ ├── react --> namespace
│ │ └── accordion --> component
│ └── react-native --> namespace
│ └── accordion --> component
└── theme --> namespace
├── web --> component
└── mobile --> component

3. Добавление зависимостей в платформенно-ориентированные компоненты

Между React и React Native есть различия. Например, они не допускают применения одинаковых свойств и типов стилей. Если все размерности в React Native не имеют единиц измерения, то в React можно использовать px, rem, em и т.д.

Для выполнения нашей задачи понадобятся базовый объект с наиболее общими значениями и платформенно-ориентированные компоненты с дополнительными свойствами.

Воспользуемся полезным шаблоном создания контекста и хука для внедрения токенов в компоненты. Чтобы заставить их работать с React и React Native, прибегнем к хитрости. Она заключается в том, что у нас будет три вида токенов.

  • Базовый токен  —  общий для обоих:
export interface BaseTokensProps {
  primaryColor: string;
  secondaryColor: string;
  borderColor: string;
  borderStyle: "solid" | "dotted" | "dashed" | undefined;
}

export const baseTokens: BaseTokensProps = {
  primaryColor: "red",
  secondaryColor: "blue",
  borderColor: "green",
  borderStyle: "solid",
};
  • Токен, дополняющий базовый для использования с React:
import { baseTokens } from "@learnbit-react/web-mobile-design-system.design-tokens.base-tokens";
import type { BaseTokensProps } from "@learnbit-react/web-mobile-design-system.design-tokens.base-tokens";

export interface ReactTokensProps extends BaseTokensProps {
  spacing: string;
  fontSize: string;
  borderWidth: string;
}

export const reactTokens: ReactTokensProps = {
  ...baseTokens,
  spacing: "15px",
  fontSize: "18px",
  borderWidth: "3px",
};
  • Токен, дополняющий базовый для использования с React Native:
import { baseTokens } from "@learnbit-react/web-mobile-design-system.design-tokens.base-tokens";
import type { BaseTokensProps } from "@learnbit-react/web-mobile-design-system.design-tokens.base-tokens";

export interface RNativeTokensProps extends BaseTokensProps {
  spacing: number;
  fontSize: number;
  borderWidth: number;
}

export const rNativeTokens: RNativeTokensProps = {
  ...baseTokens,
  primaryColor: "purple",
  secondaryColor: "gray",
  spacing: 10,
  fontSize: 12,
  borderWidth: 3,
};

Если в вашей среде разработки выполняется автозаполнение местоположения посредством относительного импорта, можете быстро исправить это, выполнив команду bit link — rewire.

4. Предоставление тем для React и React Native

В Bit тема может быть и компонентом, что позволяет легко составлять зависимости для дизайн-токенов и тем.

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

$ bit install @teambit/base-react.theme.theme-provider

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

import { createTheme } from "@teambit/base-react.theme.theme-provider";

import { reactTokens } from "@learnbit-react/web-mobile-design-system.design-tokens.react-tokens";
import type { ReactTokensProps } from "@learnbit-react/web-mobile-design-system.design-tokens.react-tokens";

const theme = createTheme<ReactTokensProps>({
  theme: reactTokens,
});

const { useTheme, ThemeProvider } = theme;
export { useTheme, ThemeProvider };
  • А вот и тема для React Native. Передадим опцию withoutCssVars: true, чтобы избежать необходимости рендеринга <div>.
import { createTheme } from "@teambit/base-react.theme.theme-provider";

import { rNativeTokens } from "@learnbit-react/web-mobile-design-system.design-tokens.rnative-tokens";
import type { RNativeTokensProps } from "@learnbit-react/web-mobile-design-system.design-tokens.rnative-tokens";

const theme = createTheme<RNativeTokensProps>({
  theme: rNativeTokens,
  withoutCssVars: true,
});

const { useTheme, ThemeProvider } = theme;

export { useTheme, ThemeProvider };

Предоставим темы аккордеонам. Теперь это просто сделать: можно импортировать компоненты-темы.

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

import { useTheme } from '@learnbit-react/web-mobile-design-system.theme.web // or .mobile in the react-native one!
import { useOpen } from '@learnbit-react/web-mobile-design-system.hooks.use-open';
import { useSelect } from '@learnbit-react/web-mobile-design-system.hooks.use-select';
import type { AccordionProps } from '@learnbit-react/web-mobile-design-system.api.accordion';

const GenericAccordionTemplate = ({ elementList } : AccordionProps) => {
  const { isOpen, toggleOpen } = useOpen();
  const { selectedId, setSelection } = useSelect();
  const {someValueToken, someValueToken} = useTheme();

  return <div_or_View style={{someProp: someValueToken}}>My styled element<div_or_View/>;
};

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

5. Создание веб- и мобильного приложений с общими зависимостями

Наконец пришло время создать веб- и мобильное приложения с использованием новых компонентов и общих зависимостей. В компонуемых приложениях (т.е. с Bit) всегда есть возможность добавить компонент и развернуть его. Поскольку здесь все является компонентом, можно добавить компонент для развертывания в Netlify или создать/добавить любые аналогичные компоненты для развертывания в любом месте. Подробнее здесь.

Веб-приложение React

Вот конечный резульат развертывания в Netlify. Посмотрим, как это сделать.

Создадим компонент приложения и установим Netlify. 

$ bit create react-app apps/react/accordion — scope learnbit-react.web-mobile-design-system

bit install react-router-dom

bit use learnbit-react.web-mobile-design-system/apps/react/accordion

bit install @teambit/cloud-providers.deployers.netlify

Теперь добавим аккордеон в код приложения.

import React from "react"; 
import { Routes, Route } from "react-router-dom";
import { Accordion } from "@learnbit-react/web-mobile-design-system.base-ui.react.accordion";
import { Item } from "@learnbit-react/web-mobile-design-system.api.accordion"; export function AccordionApp() {
return (
<>
{/* header component */}
<Routes>
<Route
path="/"
element={
<Accordion
elementList={[
new Item("Asia", "01").toObject(),
new Item("Africa", "02").toObject(),
new Item("North America", "03").toObject(),
new Item("South America", "04").toObject(),
new Item("Antarctica", "05").toObject(),
new Item("Australia / Oceania", "06").toObject(),
new Item("Europe", "07").toObject(),
]}
/>
}
/>
<Route path="/about">{/* about page component */}</Route>
</Routes>
{/* footer component */}
</>
);
}

Путем настройки приложения в accordion.react-app.ts и запуска bit tag, получим его снапшот и развернем его:

bit tag apps/react/accordion -m “First deploy”

Наконец оно развернуто!

Использование компонента React Native в Expo

expo init my-new-project 

cd my-new-project

yarn install @learnbit-react/web-mobile-design-system.base-ui.react-native.accordion @learnbit-react/web-mobile-design-system.api.accordion

Добавим компонент в файл app.js:

import {Accordion} from '@learnbit-react/web-mobile-design-system.base-ui.react-native.accordion';
import { Item } from '@learnbit-react/web-mobile-design-system.api.accordion';

import {(StyleSheet, View)} from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Accordion
        elementList={[
                new Item('Asia', '01').toObject(),
                new Item('Africa', '02').toObject(),
                new Item('North America', '03').toObject(),
                new Item('South America', '04').toObject(),
                new Item('Antarctica', '05').toObject(),
                new Item('Australia / Oceania', '06').toObject(),
                new Item('Europe', '07').toObject(),
              ]}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'flex-start',
    marginTop: 50,
    },
  });

Вот исходный код на GitHub.

Вывод

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

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Jonathan Saring: Sharing Code Between React and React Native

Предыдущая статьяОтступы в Python: так ли это плохо?
Следующая статьяЧто такое закрепление сертификата в Android