注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
useProjects フックは、高度な ToDo アプリケーションでプロジェクトデータを取得・管理するための包括的なインターフェースを提供し、特にリアルタイムのタスク統計の計算に重点を置いています。オントロジー SDK を活用して、型安全な方法でバックエンドサービスとやり取りし、コンポーネントがプロジェクトデータと集計されたタスクメトリクスにアクセスするためのシンプルなインターフェースを提供します。
このフックは、実行時の派生プロパティを実装し、タスクの総数やステータスのカテゴリー別(完了、進行中、未開始)の分布など、各プロジェクトのタスク統計を効率的に計算します。データ取得に SWR (stale-while-revalidate) を使用することで、ネットワークリクエストを最適化しながら、設定可能なキャッシュ戦略によってデータを最新の状態に保ちます。
COMPLETED、IN PROGRESS、NOT STARTED)でタスクを絞り込みます。error 状態で示し、フックはこれを isError として返します。useProjects の構造Copied!1 2 3 4 5 6export type IProject = Osdk.Instance<AdvanceTodoProject, never, PropertyKeys<AdvanceTodoProject>> & { numberOfTasks: number, numberOfCompletedTasks: number, numberOfInProgressTasks: number, numberOfNotStartedTasks: number, }
このインターフェースは、SDK の基本となるプロジェクトの型と、タスク統計用の追加プロパティを組み合わせています。
Osdk.Instance を使用してプロジェクトオブジェクトのベースタイプを取得しますuseProjects フックは、SWR のデータ取得機能と、次の処理を行うカスタム取得関数を使用します。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19const 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 を設定し、パフォーマンスを最適化します。
Copied!1 2 3 4 5const { 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(...) による加算でタスクの種類別の件数を合計します
参照実装では、ステータスの文字列リテラルと比較します。マジックストリングを避けるため、必要に応じてタスクのステータス用の定数を定義できます。
Copied!1 2 3 4 5const TASK_STATUS = { COMPLETED: "COMPLETED", IN_PROGRESS: "IN PROGRESS", NOT_STARTED: "NOT STARTED" } as const;
このパターンには、次の効果があります。
次の外部パッケージを useProjects フックとともに使用できます。
目的: 事前定義されたデータ型を持つ、アプリケーション固有の SDK 利点:
AdvanceTodoProject)を含みます目的: バックエンドのデータサービスとやり取りするためのオントロジー SDK クライアント 利点:
PropertyKeys と Osdk.Instance による型操作のユーティリティを提供しますuseOsdkClient などのフックを公開します目的: データ取得、キャッシュ、状態管理 利点:
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 51import 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 フックを使用する際は、次のシナリオと制限事項を考慮してください。
fetchPage のオプションを使用したページネーションの実装を検討してください。codingTasks と learningTasks の関係を検索します。システムに新しい種類のタスクを追加した場合は、実行時の派生プロパティのクエリを更新する必要があります。numberOfTasks は異なる種類のタスクの件数を加算して計算していますが、代わりにバックエンドで事前計算することもできます。異なる方法のパフォーマンス上のトレードオフを評価することを検討してください。次の図は、コンポーネントからのリクエストが useProjects フックを通過する流れを示しています。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15flowchart 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