13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

READYFORAdvent Calendar 2021

Day 3

SPAでのデータの流れ方を設計するための一つの考え方

Last updated at Posted at 2021-12-02

はじめまして。プロダクトエンジニアのもっさんです。
この記事は READYFORアドベントカレンダー2021 、3日目の記事です。

はじめに

READYFORでは今年の9月ごろに、プロジェクトを実行する実行者が、支援してくれた方々とのコミュニケーション及びリターンの管理機能などを管理するための機能を一新し、リリースしました。(詳細はこちら)
今回は、そこで新しく作成した SPA での State 管理についての話をします。

ページ構成

今回リリースした SPA の各ページでは、大きく分けてサーバーから取得したデータを表示するテーブル部分と、ユーザーが検索したい内容を入力するフォーム部分の 2 つに分かれます。検索内容は URL Parameter から渡され、 API のパラメータ及び、フォームのデフォルト入力値として利用されます。
このページが状態として持っている箇所は、大きく分けて以下の 3 つになります。

  • URL のパラメータ (Query)
  • 検索フォーム (Form State)
  • テーブル (Resource State)

この State を何も考えずに扱うと State 間での不整合が生まれるため(formで検索した値がテーブルにだけ反映されて、URLパラメータへの反映を忘れてしまうなど)
、ひと工夫が必要です。

fig1.png

前提として以下のライブラリ、ツールが利用されています。

Next.js

site: https://nextjs.org

vercel が提供している React.js 製のフロントエンドフレームワークです。
Next.js は、本番環境に必要なすべての機能(ハイブリッド静的およびサーバーレンダリング、TypeScript サポート、スマートバンドリング、ルートプリフェッチなど)をゼロコンフィグで利用できます。

SWR

site: https://swr.vercel.app

Next.js の開発元である vercel が提供しているデータ取得のための React Hook ライブラリです。
SWR は、まずキャッシュからデータを返してからフェッチリクエストを送り、最後に最新のデータを持ってくるという戦略を取っています。

React Hook Form

site: https://react-hook-form.com

高性能で柔軟かつ拡張可能な使いやすいフォームバリデーションライブラリです。
最小限のコードでフォームを作成でき、 TypeScript との親和性が非常に高いため、型安全なフォームを作成できます。

openapi-typescript

site: https://github.com/drwpow/openapi-typescript

openapi の定義から TypeScript の型を生成するための node ライブラリです。

pathpida

site: https://github.com/aspida/pathpida

Next.js の Page コンポーネントの定義から自動的に URL の型定義ファイルを生成するライブラリです。

型の依存関係

fig3.png

基本となる型定義は openapi-typescript を用いて、schema.yaml から自動的に型定義ファイルを生成しています。生成された型をもとに、API Query の型、Form で扱うための型及び、ページの Query Parameter の型を定義します。基本的には生成された型からエイリアスを貼り、必要な型を抽出、結合して、利用します。ただし、Form においては input の型の都合上、例外的に独自の型を宣言する場合がありますが、その場合でも型の宣言・利用は Form State と Form View 内で完結します。Form Hook が Props として受け取る型は加工せず、また、他のパートへの型の変更の影響を与えることはありません。

データフロー

データフローの考え方において、以下の図のようなレイヤに分けて考えます。各モジュール、親コンポーネントのそれぞれのレイヤで、どのような責務を持つかを定義します。

fig2.png

Query

このレイヤーでは、 Next.js の useRouter を利用して URL パラメータにアクセスします。
Query では以下の責務を持ちます。

  • ページに必要なパラメータの型定義(pathpida を利用)
  • パラメータの型の変換と型の保証
  • 必須パラメータのチェック(存在しない場合はエラーへの遷移)
  • Template レイヤへの値の受け渡し

Template

このレイヤでは Query レイヤからのデータを受け取り Hook へのデータの受け流しを行います。また Hook と View の繋ぎ込みを行います。
表示の出し分け等は行いますが、基本的にロジックを持ちません。

Form State

ユーザの操作により、変化する値を管理するレイヤーです。検索フォームの入力や、ページ情報、ソート情報の保持、ハンドラ定義などもこのレイヤでおこないます。
実装上は Hook として切り出していて、 State 管理には React Hook Form を利用しています。UI の制限により、State で保持する値や型を変更している場合には、この State 内に閉じた型を宣言します。データの変化がある場合は Next.js の push アクションを経由して URL の変更し、 Query レイヤーの State の変更します。

Resource State

受け取った Props を元に API からリソースの取得をおこないます。Form State と同じく実装上は Hook として切り出していて、State の管理及びリソースの取得には SWR を利用しています。 ユーザの操作によって検索条件が変更になった場合、Form の State から直接パラメータを受け取ることはせず、必ず Query と Template を経由してパラメータを受け取ります。

まとめ

今回のまとめた複数 State 管理の考え方は、 Flux のイメージから設計されています。複数モジュール間におけるデータの流れを一方向にし、ユーザの操作による検索パラメータの適用を push アクションに集約しています。これにより、複数の State の整合性が保ちやすくなりました。

明日はTakepepeさんによるフロントエンドのテストに関する話です!

13
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?