Closed4

Expo(React Native) + Next.jsでアプリとWebでコードをできるだけ共通化して開発してみる【Tamagui/Solito】

t0m0120t0m0120

ExpoはWebに対応しているが、SPAかSSGでしかリリースできない。
SEOのためにSSRやISRしたいので、WebはNext.jsで開発する。

できるだけWebとNativeでコードを共通化して、工数を減らしたい。
solitoとtamaguiの組み合わせで、コードをできるだけ共通化して開発してみる。
https://solito.dev/
https://tamagui.dev/

t0m0120t0m0120

tamaguiのnpm createでFree Expo + Next テンプレートを使う。

$ npm create tamagui@latest
Creating tamagui app...
✔ Project name: test
? Pick a template: › - Use arrow-keys. Return to submit.
❯  Free - Expo + Next in a production ready monorepo

作成されたフォルダでyarn install

$ cd ./test
$ yarn

下記のコマンドでそれぞれの開発環境が立ち上がる。

// Next.js
$ yarn web
// Expo 
$ yarn native
Web Native
t0m0120t0m0120

作られたフォルダ構成は下記の様な形。
expoとnextに関してはいつもどおりの中身。
packagesに共通のものを入れていき、appsそれぞれで使っていく形っぽい。

..
├── apps                  # アプリケーション関連のコード
│   ├── expo              # Expoアプリケーション(モバイル版)
│   └── next              # Next.jsアプリケーション(Web版)
├── packages              # 共通のライブラリやコンポーネント
│   ├── app               # アプリケーション関連の共有ライブラリ
│   ├── config            # 設定ファイルや設定関連のコード
│   └── ui                # 共通のUIコンポーネントやスタイル

それぞれのホームスクリーンのtsxを見てみると、HomeScreenのComponentを読み込んでいるだけ。

expo/app/index.tsx
import { HomeScreen } from 'app/features/home/screen'
import { Stack } from 'expo-router'

export default function Screen() {
  return (
    <>
      <Stack.Screen
        options={{
          title: 'Home',
        }}
      />
      <HomeScreen />
    </>
  )
}
next/app/page.tsx
'use client'
import { HomeScreen } from 'app/features/home/screen'
export default HomeScreen

全体的に、TamaguiでScreenを表示しているが、リンク部分だけはsolitoを使っています。
solitoのuseLinkHookを使って、Next.js/Expo両方に対応したリンクのPropsを作成し渡すようにしています。

app/features/home/screen.tsx
import { useLink } from 'solito/navigation'
export function HomeScreen({ pagesMode = false }: { pagesMode?: boolean }) {
  const linkTarget = pagesMode ? '/pages-example-user' : '/user'
  const linkProps = useLink({
    href: `${linkTarget}/nate`,
  })

  return (
    <YStack f={1} >
      <Button {...linkProps}>Link to user</Button>
    </YStack>
  )
}
t0m0120t0m0120

Appディレクトリに共通Componentなどを入れていくようになっている。

app/
├── features/
├── provider/
└──  navigation/

featuresはScreenではなく、機能単位で分割されたComponent.
providerはラップする必要のある処理を置くフォルダ。

このスクラップは1日前にクローズされました