ユースケース開発React アプリケーションのためのOSDKチュートリアル:高度なToDoアプリケーションUseProjectsフック

注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

useProjects フック

useProjects フックは、高度な ToDo アプリケーションでプロジェクトデータを取得・管理するための包括的なインターフェースを提供し、特にリアルタイムのタスク統計の計算に重点を置いています。オントロジー SDK を活用して、型安全な方法でバックエンドサービスとやり取りし、コンポーネントがプロジェクトデータと集計されたタスクメトリクスにアクセスするためのシンプルなインターフェースを提供します。

このフックは、実行時の派生プロパティを実装し、タスクの総数やステータスのカテゴリー別(完了、進行中、未開始)の分布など、各プロジェクトのタスク統計を効率的に計算します。データ取得に SWR (stale-while-revalidate) を使用することで、ネットワークリクエストを最適化しながら、設定可能なキャッシュ戦略によってデータを最新の状態に保ちます。

useProjects の参照コードを表示する。

主な機能

  • サーバー側の集計: 実行時の派生プロパティのパターンを使用してサーバー側で集計を行い、データ転送を最小限に抑えます。
  • 型安全性: TypeScript インターフェースとオントロジー SDK の型ユーティリティにより、全体を通して型安全性を確保します。
  • ステータスによる絞り込み: サーバー側の集計中に、ステータスのリテラル値(COMPLETED、IN PROGRESS、NOT STARTED)でタスクを絞り込みます。
  • エラー処理: 取得の失敗を SWR の error 状態で示し、フックはこれを isError として返します。
  • パフォーマンスの最適化: カスタムオプションで SWR を設定し、ネットワークリクエストとキャッシュを最適化します。
  • シンプルな戻り値のインターフェース: コンポーネントが容易に利用できる、適切に構造化された戻り値オブジェクトを提供します。
  • コンポーネントで使いやすいデータ: 追加の処理を行わずにコンポーネントで利用できる形式にデータを変換します。

useProjects の構造

インターフェース定義

Copied!
1 2 3 4 5 6 export type IProject = Osdk.Instance<AdvanceTodoProject, never, PropertyKeys<AdvanceTodoProject>> & { numberOfTasks: number, numberOfCompletedTasks: number, numberOfInProgressTasks: number, numberOfNotStartedTasks: number, }

このインターフェースは、SDK の基本となるプロジェクトの型と、タスク統計用の追加プロパティを組み合わせています。

  1. Osdk.Instance を使用してプロジェクトオブジェクトのベースタイプを取得します
  2. タスク統計用に、厳密に型付けされた数値プロパティを追加します
  3. コンポーネントが内部実装を気にせずに使用できる統一された型を作成します

データ取得

useProjects フックは、SWR のデータ取得機能と、次の処理を行うカスタム取得関数を使用します。

  1. 実行時の派生プロパティを使用して、1回のリクエストでタスク統計を含むプロジェクトを取得します
  2. 複雑な絞り込みと集計の処理をサーバー側で実行します
  3. 結果を期待されるインターフェース形式にマッピングします
Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const fetcher = useCallback(async () => { const projectsPage = await client(AdvanceTodoProject) .withProperties({ // タスクの種類ごとの件数を求める実行時の派生プロパティ定義 }) .fetchPage(); const projects: IProject[] = projectsPage.data.map((project) => { return { ...project, numberOfTasks: Number(project.numberOfCodingTasks) + Number(project.numberOfLearningTasks), numberOfCompletedTasks: Number(project.numberOfCompletedCodingTasks) + Number(project.numberOfCompletedLearningTasks), numberOfInProgressTasks: Number(project.numberOfInProgressCodingTasks) + Number(project.numberOfInProgressLearningTasks), numberOfNotStartedTasks: Number(project.numberOfNotStartedCodingTasks) + Number(project.numberOfNotStartedLearningTasks), }; }); return projects; }, [client]);

SWR の設定

このフックは、カスタムオプションで SWR を設定し、パフォーマンスを最適化します。

Copied!
1 2 3 4 5 const { data, isLoading, isValidating, error } = useSWR<IProject[]>( "projects", fetcher, { revalidateOnFocus: false } );

上記の例の設定では、次の動作になります。

  • revalidateOnFocus: ウィンドウが再びフォーカスを得たときの自動再検証を無効にします。

戻り値

Copied!
1 2 3 4 5 6 { projects: IProject[], // プロジェクトの配列(データがない場合は空の配列) isLoading: boolean, // データの初回読み込み中は true isValidating: boolean, // バックグラウンドでの再検証中は true isError: Error | undefined, // リクエストに失敗した場合のエラーオブジェクト }

このシンプルなインターフェースにより、コンポーネントはさまざまな状態を処理し、適切な UI を表示するために必要な情報をすべて取得できます。

実装

実行時の派生プロパティ

実行時の派生プロパティは、オブジェクト全体を取得してクライアント側で値を計算する代わりに、実行時に必要なデータだけを計算・取得できる強力なパターンです。

このフックでは、実行時の派生プロパティを使用して、サーバー上で直接タスク統計を計算します。

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .withProperties({ "numberOfCodingTasks": (baseObjectSet) => baseObjectSet.pivotTo("codingTasks").aggregate("$count"), "numberOfCompletedCodingTasks": (baseObjectSet) => baseObjectSet.pivotTo("codingTasks").where({ "status": { $eq: "COMPLETED" }, }).aggregate("$count"), "numberOfLearningTasks": (baseObjectSet) => baseObjectSet.pivotTo("learningTasks").aggregate("$count"), "numberOfCompletedLearningTasks": (baseObjectSet) => baseObjectSet.pivotTo("learningTasks").where({ "status": { $eq: "COMPLETED" }, }).aggregate("$count"), // 進行中および未開始の件数を求める追加のプロパティ... })

この実装では、オントロジー SDK のいくつかの主要なパターンを使用しています。

  • pivotTo: プロジェクトから関連するタスクへ移動します

    • 目的: プロジェクトとそのタスク間の関係をたどります
    • 利点: 複雑な関係管理を行わずに、関連データを結合できます
  • where: フィルターを適用して、特定のステータスを持つタスクを選択します

    • 目的: プロパティの条件に基づいて集合を絞り込みます
    • 利点: サーバー側で絞り込むことで、データ転送を削減します
  • aggregate: 絞り込んだ集合の件数を計算します

    • 目的: サーバー側でデータを集計します
    • 利点: 集計結果のみを返すことで、データ転送を最小限に抑えます
  • クライアント側の合計: 結果をマッピングする際に、Number(...) による加算でタスクの種類別の件数を合計します

    • 目的: コーディングタスクと学習タスクの個別の件数を1つの合計にまとめます
    • 利点: 実行時の派生プロパティごとの集計を1つに保ちながら、合算した合計も利用できるようにします

利点

  • パフォーマンスの最適化: 件数の計算をサーバー側で行い、データ転送を削減します
  • クライアント側の計算の削減: クライアントは最終的な件数のみを受け取ります
  • 単一リクエストのパターン: すべての統計を1回のリクエストで取得します
  • 宣言的なデータ要件: コード自体が仕様を説明します

ステータスの列挙用の定数

参照実装では、ステータスの文字列リテラルと比較します。マジックストリングを避けるため、必要に応じてタスクのステータス用の定数を定義できます。

Copied!
1 2 3 4 5 const TASK_STATUS = { COMPLETED: "COMPLETED", IN_PROGRESS: "IN PROGRESS", NOT_STARTED: "NOT STARTED" } as const;

このパターンには、次の効果があります。

  • ステータス値を持つ、厳密に型付けされた定数オブジェクトを作成します
  • 意味のある変数名を使用することで、コードの可読性を向上させます
  • ステータス定義を一元化し、コードベース全体で一貫して使用できるようにします
  • ステータスの文字列値の入力ミスや不整合を防ぎます

外部パッケージ

次の外部パッケージを useProjects フックとともに使用できます。

@advanced-to-do-application/sdk

目的: 事前定義されたデータ型を持つ、アプリケーション固有の SDK 利点:

  • プロジェクトのデータモデル(AdvanceTodoProject)を含みます
  • プロジェクトのプロパティと関係に、型付きでアクセスできます
  • アプリケーション全体で一貫して型を強制できます
  • アプリケーションのデータモデルとのインタラクションを簡素化します

@osdk/client & @osdk/react

目的: バックエンドのデータサービスとやり取りするためのオントロジー SDK クライアント 利点:

  • データモデル用の型安全なインターフェースを提供します
  • 複雑な絞り込みを含む、構造化されたデータクエリを実行できます
  • サーバー側の計算のために、実行時の派生プロパティのパターンをサポートします
  • PropertyKeys と Osdk.Instance による型操作のユーティリティを提供します
  • クライアントインスタンスにアクセスするための useOsdkClient などのフックを公開します

useSWR

目的: データ取得、キャッシュ、状態管理 利点:

  • プロジェクトデータを取得してキャッシュするための洗練された方法を提供します
  • 読み込み状態とエラー状態を自動的に処理します
  • 設定可能なオプションを備えた、組み込みの再検証戦略を提供します
  • インテリジェントなキャッシュによってネットワークリクエストを削減します
  • 自動エラー処理によってデータ取得を簡素化します

使用例

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import React from 'react'; import useProjects, { type IProject } from '../dataServices/useProjects'; const ProjectsPage: React.FC = () => { const { projects, isLoading, isError } = useProjects(); if (isLoading) return <div>Loading projects...</div>; if (isError) return <div>Error loading projects: {isError.message}</div>; return ( <div className="projects-container"> <h1>Projects ({projects.length})</h1> {projects.length === 0 ? ( <div className="empty-state">No projects found</div> ) : ( <div className="projects-grid"> {projects.map(project => ( <ProjectCard key={project.$primaryKey} project={project} /> ))} </div> )} </div> ); }; // データを使用する ProjectCard コンポーネント const ProjectCard: React.FC<{ project: IProject }> = ({ project }) => { return ( <div className="project-card"> <h3>{project.name}</h3> <div className="progress-bar"> <div className="progress-fill" /> </div> <div className="task-stats"> <div>Total Tasks: {project.numberOfTasks}</div> <div>Completed: {project.numberOfCompletedTasks}</div> <div>In Progress: {project.numberOfInProgressTasks}</div> <div>Not Started: {project.numberOfNotStartedTasks}</div> </div> </div> ); }; export default ProjectsPage;

エッジケースと制限事項

useProjects フックを使用する際は、次のシナリオと制限事項を考慮してください。

  • 空の結果の処理: この実装は空の結果セットを適切に処理し、エラーをスローする代わりに空の配列を返します。
  • エラーからの回復: このフックは SWR のデフォルトのエラー再試行動作に依存していますが、複雑な障害シナリオでは追加の処理が必要になる場合があります。
  • ページネーションの制限事項: 現在の実装では、すべてのプロジェクトを一度に取得するため、プロジェクト数が多い場合に問題になる可能性があります。大規模なデータセットでは、fetchPage のオプションを使用したページネーションの実装を検討してください。
  • タスクの種類への依存関係: このフックは、特に codingTasks と learningTasks の関係を検索します。システムに新しい種類のタスクを追加した場合は、実行時の派生プロパティのクエリを更新する必要があります。
  • 更新のポーリング: このフックには、更新を確認するポーリングが実装されていません。リアルタイムの更新が必要なアプリケーションでは、ポーリングの仕組みや WebSocket 統合の追加を検討してください。
  • タスクステータスの列挙との結合: このフックは、特定のタスクステータス値と密接に結び付いています。バックエンドでこれらの値を変更した場合は、フロントエンドもそれに応じて更新する必要があります。
  • 数学的な派生プロパティ: numberOfTasks は異なる種類のタスクの件数を加算して計算していますが、代わりにバックエンドで事前計算することもできます。異なる方法のパフォーマンス上のトレードオフを評価することを検討してください。

データフロー

次の図は、コンポーネントからのリクエストが useProjects フックを通過する流れを示しています。

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 flowchart TD A[Component renders] --> B[useProjects hook called] B --> C[SWR checks cache] C -->|Cache hit| D[Return cached projects] C -->|Cache miss| E[Call fetcher function] E --> F[Fetch projects with runtime-derived properties] F --> G[Transform data] G --> H[Return projects] H --> I[Component renders projects] subgraph "Runtime-derived properties processing" F1[Fetch base projects] --> F2[Compute task counts] F2 --> F3[Filter by status] F3 --> F4[Aggregate counts] end