React 18への予習シリーズ:Suspenseを復習しよう に続く、React18シリーズです。今回は今後のアプリ作りに影響しそうなトランジションを取り上げます。
トランジションはReact 16.13で実験的機能として実装されました、公式ページの並列的 UI パターン(実験的機能)に詳しい説明があります。しかし、React 18 alphaではAPI等が変更されているので注意してください。
従来のReact
まずは、Suspenseを使った普通のReactアプリです。
このアプリは
- ロードされるとFirst PageとNextボタンが表示されます
- Nextボタンを押すと、画面が切り替わり
Loading...
と表示されます。同時にAPIサーバーからデータ取得が開始されます - APIサーバーからデータが取得できると、Next Pageとサーバーから取得された文字列(Hello!)が表示されます。
以下は画面の動きをアニメーションGIFにしたものです。
コードは以下のようになります、Suspenceに付いてはReact 18への予習シリーズ:Suspenseを復習しようを読んでください。
import React, { useState, Suspense } from 'react'
import useSWR from 'swr'
const URL = "APIサーバーのURL"
type MessageType = {message: string}
export const App: React.FC = () => {
const [pageNo, setPageNo] = useState(1)
return (
<Suspense fallback={<p>Loading...</p>}>
{pageNo === 1 && <FirstPage pushed={() => setPageNo(2)} />}
{pageNo === 2 && <NextPage />}
</Suspense>
)
}
type FirstPageProps = { pushed: () => void }
const FirstPage: React.FC<FirstPageProps> = ({pushed}) => {
return (
<>
<h2>First Page</h2>
<button onClick={pushed}>Next</button>
</>
)
}
const NextPage: React.FC = () => {
const {data} = useSWR<MessageType>(URL, { suspense: true })
return (
<>
<h2>Next Page</h2>
<p>{data?.message}</p>
</>
)
}
トランジション
さて、次はReact 18で正式APIになるトランジション(useTransition)を使ったプログラムです。
このアプリは
- ロードされるとFirst PageとNextボタンが表示されます
- Nextボタンを押すと、画面はそのままでボタンの文字が
Next...
と表示されます。同時にAPIサーバーからデータ取得が開始されます - APIサーバーからデータが取得できると、Next Pageとサーバーから取得された文字列(Hello!)が表示されます。
以下は画面の動きをアニメーションGIFにしたものです。
従来のアプリとの違いは、ボタンを押すと次の画面コンポーネントが実行されているのですが、画面表示は最初のページのな点です。ボタンを押しても次のページのコンテンツが準備できるまでは現在の画面が表示されていてLoading...
のような無意味な画面は表示されない事です。また、コンテンツ取得中も最初のページのコンテンツが読めるのはUX的にも良いように思えます。
import React, { useState, useTransition, Suspense } from 'react'
import useSWR from 'swr'
const URL = "APIサーバーのURL"
type MessageType = {message: string}
export const App: React.FC = () => {
const [pageNo, setPageNo] = useState(1)
return (
<Suspense fallback={<p>Loading...</p>}>
{pageNo === 1 && <FirstPage pushed={() => setPageNo(2)} />}
{pageNo === 2 && <NextPage />}
</Suspense>
)
}
type FirstPageProps = {
pushed: () => void
}
const FirstPage: React.FC<FirstPageProps> = ({pushed}) => {
const [isPending, startTransition] = useTransition() // ← ①
return (
<>
<h2>First Page</h2>
<button onClick={() => startTransition(() => pushed())}> // ← ②
{isPending ? "Next..." : "Next"} // ← ③
</button>
</>
)
}
const NextPage: React.FC = () => {
const {data} = useSWR<MessageType>(URL, { suspense: true })
return (
<>
<h2>Next Page</h2>
<p>{data?.message}</p>
</>
)
}
コードは上のようになります。「従来のReact」コードとの違いはごくわずかです。
- ① トランジションを使うには、useTransitionホックを使います。戻り値はトランジション中(通信中など)を表す論理値isPendingとトランジション開始関数startTransitionです
- 注意 : useTransitionの引数、戻り値はReact 18 alphaでは並列的 UI パターン(実験的機能) とは変わっています。
- ② ボタンを押したイベント処理でトランジションを開始するstartTransition関数を実行します、引数は、ページ遷移(トランジション)を起こすState変更処理を指定します。これによりサーバーからのデータ取得が終わるまでページは変化しなくなります。
- ③ トランジション中を表すisPendingを使ってボタンの文字を変えています。サーバーからデータ取得中になっているかを示す事でユーザーに安心感を与えています。
これだけで、新しいスタイルのページ遷移ができます!
さて、なぜこれがConcurrent Mode(並列モード)によって可能になったのかは、次回解説したいと思っています。
おまけ : 2021-07-14日時点でのReact 18 alpha + TypeScriptの環境構築方法
React 18 alphaでTypeScriptが使える環境の構築の公式ドキュメントはありません。 今回はHow to use TypeScript with React 18 alphaを参考にしました。
Alphaバージョンなので今後変更されるかも知れませんので、試すときは最新のドキュメントも参考にしてください。
インストール
まず、create-react-appを使ってTypeScriptベースの開発環境を作ります。テンプレートcra-template-ey-officeは私が作ったものですがtypescriptでも良いと思います。
プロジェクト作成後、React18alphaを --force
オプションを付けてインストールします。
$ npx create-react-app react18alpha --template cra-template-ey-office
$ cd react18alpha
$ npm install react@alpha react-dom@alpha --force
設定ファイルの変更
TypeScriptの設定ファイルtsconfig.json
以下のようにtypesオプションを追加します。
- "jsx": "react-jsx"
+ "jsx": "react-jsx",
+ "types": ["react/next", "react-dom/next"]
React18のrootAPIへの変更
index.tsxにあるReactDOM.render
をReact18用のReactDOM.createRoot
に変更します。
- ReactDOM.render(<App />, document.getElementById('root'))
+ ReactDOM.createRoot(
+ document.getElementById('root') as HTMLElement).render(<App />)
実行は今まで通りです
npm start