Open6

Vite + React + GraphQL + TanStack(React Query, React Location) 素振り

i_am_master_yodai_am_master_yoda

Vite

Vite プロジェクトを作成

yarn create vite

ライブラリは React(ts) を選択

? Select a framework: › - Use arrow-keys. Return to submit.
    vanilla
    vue
❯   react
    preact
    lit
    svelte

起動

yarn && yarn dev

ready in 734ms.
早い。

i_am_master_yodai_am_master_yoda

React Query

インストール

yarn add react-query

App.tsx を編集

import { QueryClient, QueryClientProvider } from 'react-query'

const queryClient = new QueryClient()

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <>App.tsx</>
    </QueryClientProvider>
  )
}

export default App

i_am_master_yodai_am_master_yoda

GraphQL

graphql と graphql codegen の準備をする。
@graphql-codegen/typescript-react-query というpackageを使って types と hooks を自動生成する。(codegen ドキュメントのデザインが変わっていてびっくりした)

インストール

yarn add @graphql-codegen/cli @graphql-codegen/typescript-react-query @graphql-codegen/typescript-operations graphql graphql-request

codegen.yml

fetch hooks の設定
GraphQL API には SpaceX のAPI endpoint を使用してみる。

overwrite: true
schema:
  - https://api.spacex.land/graphql/ 
documents:
  - ./src/**/*.graphql
  - ./src/**/*.graphql
generates:
  ./src/generated/index.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      fetcher: graphql-request

generate

graphql-codegen -r --config codegen.yml

src/generated/index.ts にファイルが生成される。

生成された fetcher hooks を使う

function App() {
  return (
  <QueryClientProvider client={queryClient}>
    <Component />
  </QueryClientProvider>
  )
}

const Component = () => {
  const { data, isLoading, isError } = useLaunchesPastQuery(client)

  if (isLoading) return <>Loading...</>
  if (isError) return <>Error(:</>

  return (
    <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center', width: "100%", height: '100vh'}}>
      <div>
        {data?.launchesPast?.map(launch => (
          <div key={launch?.mission_name}>
            <div style={{ marginBottom: '8px' }}>
              <p style={{ margin:0 }}>Mission name: {launch?.mission_name}</p>
              <p style={{ margin:0 }}>Launch date: {launch?.launch_date_local}</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

無事データを取得できた(めちゃくちゃ見辛い...

i_am_master_yodai_am_master_yoda

React Location

インストール

yarn add react-location

セットアップ

import {  Link, MakeGenerics,  Outlet,  ReactLocation, Route,  Router,  useMatch } from "react-location";

const location = new ReactLocation();
const routes: Route[] = [
  {
    path: "/",
    element: 'This is Home',
  },
  {
    path: "/launches",
    element: <Component />
  }
]

function App() {
  return (
  <QueryClientProvider client={queryClient}>
    <Router routes={routes} location={location}>
      <div style={{ maxWidth: '960px', margin: '0 auto' }}>
        <h2>SpaceX Launches</h2>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/launches">Launches list</Link></li>
        </ul>
        <Outlet />
      </div>
    </Router>
  </QueryClientProvider>
  )
}

いい感じに遷移 and データの取得が出来てそう

i_am_master_yodai_am_master_yoda

React Location の Route loader を使う

Route loader を使うことで preload を実装することができる。

route の定義すオブジェクト内で実装するため、先ほど作った fetcher hooks は使えない。
新たにroute preload 用の fetcher を graphql-request で生成する。

yarn add graphql-request

codegen.yml の更新

overwrite: true
schema:
  - https://api.spacex.land/graphql/ 
documents:
  - ./src/**/*.graphql
  - ./src/**/*.graphql
generates:
  ./src/generated/index.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-query
    config:
      fetcher: graphql-request

  # 追加
  ./src/generated/routes/index.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-graphql-request

生成された sdk をつかって loader を実装する

const sdk = getSdk(client)

function App() {
  const routes: Route<LocationGenerics>[] = [
    {
      path: "/",
      element: 'This is Home',
    },
    {
      path: "/launches",
      element: <Component />,
      loader: async () =>  {
        const { launchesPast } = await sdk.LaunchesPast()
        return {
          launchesPast: {
            data: launchesPast
          }
        }
      }
    }
  ]

Router にキャッシュの期間を指定

    <Router routes={routes} location={location} defaultLinkPreloadMaxAge={1000}>
      ....
    </Router>

Hover時に preload しているため、高速な遷移が実現できている。