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

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

useAdmin フック

useAdmin フックは、Foundry Admin API のユーザーデータを操作する機能を提供するカスタム React フックです。現在認証されているユーザーの取得、複数のユーザーの一括取得、プロフィール画像の管理など、ユーザー関連の操作を一元化します。

このフックは SWR (stale-while-revalidate) を活用して、効率的なデータ取得とキャッシュ戦略を実現します。これにより、ネットワークリクエストを最適化し、アプリケーション全体でユーザーデータに対する一貫したインターフェースを提供します。SWR と Foundry Admin SDK を組み合わせることで、読み込み状態、キャッシュ、エラー処理を自動的に管理する堅牢なユーザー管理ソリューションを実現します。

useAdmin のリファレンスコードを確認する。

主な機能

  • React の useCallback を使用して関数をメモ化し、不要な再レンダリングを防いでパフォーマンスを最適化します
  • SWR によるインテリジェントなキャッシュ戦略を実装し、API 呼び出しを最小限に抑えてアプリケーションの応答性を向上させます
  • プロフィール画像の blob URL を作成し、バイナリー画像データを効率的にレンダリングできるようにします
  • ユーザーIDの配列が空の場合は、リクエストを送信せずに空の結果を返して適切に処理します
  • ユーザーの一括取得をサポートし、複数のユーザーを読み込む必要がある場合の API 呼び出し回数を削減します
  • 読み込み、検証、エラーの状態を提供し、UI で包括的なフィードバックを実現します
  • TypeScript を使用して、すべてのデータ構造と関数シグネチャに厳密な型付けを行います

useAdmin の構造

インターフェース定義

Copied!
1 2 3 interface UserDetails { [key: string]: User; }

このインターフェースは、文字列キー(ユーザーID)を User オブジェクトに対応付ける辞書型の構造を定義します。このパターンにより、次のことが可能になります。

  • ID によるユーザーの高速な O(1) 参照
  • 複数のユーザーオブジェクトの効率的な保存
  • 複数のソース(キャッシュと API)からのユーザーデータの容易な集計

データ取得

useAdmin フックは、いくつかのデータ取得戦略を実装しています。

  1. 現在のユーザーの取得:
Copied!
1 2 3 4 const getCurrentUserDetails = useCallback(async () => { const user: User = await getCurrent(client); return { "currentUser": user }; }, [client]);
  1. プロフィール画像の取得:
Copied!
1 2 3 4 5 6 const getCurrentProfilePictureUrl: (user: User) => Promise<string> = useCallback(async (user) => { const profilePictureResponse = await profilePicture(client, user.id); const blob = await profilePictureResponse.blob(); return URL.createObjectURL(blob); }, [client]);
  1. キャッシュを最適化したユーザーの一括取得。

CRUD 操作

  1. 読み取り(現在のユーザー):
    • getCurrentUserDetails:現在認証されているユーザーを取得します。
    • 主要な SWR フックが現在のユーザーを取得し、その状態を維持します。
  2. 読み取り(プロフィール画像):
    • getCurrentProfilePictureUrl:ユーザーのプロフィール画像の blob URL を取得します。
  3. 読み取り(複数のユーザー):
    • getBatchUserDetails:キャッシュの最適化により、複数のユーザーを効率的に取得します。

useAdmin フックは読み取り操作に重点を置いており、ユーザーデータの作成、更新、削除操作は実装していません。

戻り値

useAdmin フックは、次の構造を持つオブジェクトを返します。

Copied!
1 2 3 4 5 6 7 8 9 { users: UserDetails | undefined; // 取得したすべてのユーザーを含むオブジェクト currentUser: User | undefined; // 現在ログインしているユーザー isLoading: boolean; // データ取得時の読み込み状態 isValidating: boolean; // データを再検証中かどうか isError: Error | undefined; // エラーがある場合のエラー状態 getBatchUserDetails: (userIds: string[]) => Promise<UserDetails>; // 複数のユーザーを取得する関数 getCurrentProfilePictureUrl: (user: User) => Promise<string>; // ユーザーのプロフィール画像の URL を取得する関数 }

実装

キャッシュ優先のデータ取得戦略

useAdmin フックは、ユーザーデータの取得にキャッシュ優先戦略を実装しています。

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 const getBatchUserDetails: (userIds: string[]) => Promise<UserDetails> = useCallback(async (userIds) => { const cachedUsers: UserDetails = {}; const usersToFetch: string[] = []; // 要求されたユーザーごとに、まずキャッシュを確認します userIds.forEach((userId) => { const cachedUser: State<unknown, unknown> | undefined = cache.get(`user-${userId}`); if (cachedUser && cachedUser.data) { cachedUsers[userId] = cachedUser.data as User; } else { usersToFetch.push(userId); } }); // キャッシュに見つからなかったユーザーのみを取得します if (usersToFetch.length > 0) { const usersRequest = await getBatch(client, usersToFetch.map((userId) => ({ userId }))); // 後で使用できるように結果をキャッシュに保存します Object.entries(usersRequest.data).forEach(([userId, user]) => { cachedUsers[userId] = user; mutate(`user-${userId}`, user, { revalidate: false }); }); } return cachedUsers; }, [cache, client]);

この戦略は、次の手順でパフォーマンスを向上させます。

  1. 要求されたデータがキャッシュに存在するかを確認します
  2. すでに利用可能なデータ以外のデータのみを取得します
  3. 新たに取得したデータでキャッシュを更新します
  4. キャッシュされたデータと新たに取得したデータを組み合わせて返します

blob URL の作成パターン

useAdmin フックは、API レスポンスを blob URL に変換することで、バイナリーデータ(プロフィール画像)を処理するパターンを実装しています。

Copied!
1 2 3 4 5 6 const getCurrentProfilePictureUrl: (user: User) => Promise<string> = useCallback(async (user) => { const profilePictureResponse = await profilePicture(client, user.id); const blob = await profilePictureResponse.blob(); return URL.createObjectURL(blob); }, [client]);

このパターンは、次の処理を行います。

  1. API からバイナリーデータを取得します
  2. レスポンスを blob オブジェクトに変換します
  3. 画像のソースとして使用できる一時的な URL を作成します
  4. UI でバイナリーデータを効率的にレンダリングできるようにします

外部パッケージ

次の外部パッケージを useAdmin フックと併用できます。

@osdk/react (useOsdkClient)

目的: オントロジー SDK のクライアントインスタンスにアクセスするための React フック 利点:

  • すべての API 呼び出しに必要な、認証済みクライアントへのアクセスを提供します
  • 手動で props を階層間で受け渡すことなく、認証コンテキストを管理します
  • アプリケーション全体で一貫した API アクセスを保証します
  • Foundry の認証メカニズムと統合します

@osdk/foundry.admin

目的: ユーザー関連のサービスを操作するための Foundry Admin SDK です。 利点:

  • User 型を使用して、ユーザーデータの型安全なインターフェースを提供します
  • ユーザー操作に特化した関数(getCurrent、profilePicture、getBatch)を含みます
  • 複雑な API インタラクションを抽象化し、シンプルな関数呼び出しにします
  • Foundry のバックエンドサービスおよびデータモデルとの互換性を保証します

SWR: useSWR, mutate, useSWRConfig

目的: データ取得、キャッシュ、状態管理のためのライブラリー 利点:

  • シンプルな API で、データの取得、キャッシュ、再検証を簡潔に行う方法を提供します
  • 追加のコードなしで、読み込み、エラー、検証の状態を自動的に管理します
  • 楽観的 UI 更新とバックグラウンドでの再検証戦略を実装します
  • アプリケーション全体でアクセスおよび操作できるグローバルキャッシュを提供します
  • インテリジェントなキャッシュによって不要なネットワークリクエストを削減します

使用例

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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import useAdmin from '../dataServices/useAdmin'; import { useState, useEffect } from 'react'; import type { User } from '@osdk/foundry.admin'; // 例1:現在のユーザー情報をプロフィール画像とともに表示する function UserProfile() { const { currentUser, isLoading, isError, getCurrentProfilePictureUrl } = useAdmin(); const [profilePicUrl, setProfilePicUrl] = useState<string | null>(null); useEffect(() => { async function loadProfilePic() { if (currentUser) { try { const url = await getCurrentProfilePictureUrl(currentUser); setProfilePicUrl(url); } catch (error) { console.error("Failed to load profile picture", error); } } } loadProfilePic(); }, [currentUser, getCurrentProfilePictureUrl]); if (isLoading) return <div>Loading user information...</div>; if (isError) return <div>Error loading user information</div>; if (!currentUser) return <div>No user found</div>; return ( <div className="user-profile"> {profilePicUrl && <img src={profilePicUrl} alt="Profile" className="profile-pic" />} <h1>{currentUser.username}</h1> <p>Email: {currentUser.email ?? "Not set"}</p> <p>User ID: {currentUser.id}</p> </div> ); } // 例2:共同作業者の一覧を表示する function CollaboratorsList({ userIds }: { userIds: string[] }) { const { getBatchUserDetails, isLoading } = useAdmin(); const [users, setUsers] = useState<Record<string, User>>({}); const [error, setError] = useState<unknown>(null); useEffect(() => { async function fetchUsers() { if (userIds.length > 0) { try { const fetchedUsers = await getBatchUserDetails(userIds); setUsers(fetchedUsers); } catch (err) { setError(err); } } } fetchUsers(); }, [userIds, getBatchUserDetails]); if (isLoading) return <div>Loading collaborators...</div>; if (error) return <div>Error loading collaborators</div>; return ( <div className="collaborators"> <h2>Project Collaborators</h2> <ul> {Object.entries(users).map(([userId, user]) => ( <li key={userId} className="collaborator-item"> <span className="name">{user.username}</span> <span className="email">{user.email ?? "Not set"}</span> </li> ))} </ul> </div> ); }

エッジケースと制限事項

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

  • プロフィール画像のメモリー管理: このフックは URL.createObjectURL() を使用してプロフィール画像の blob URL を作成しますが、これらの URL の解放は処理しません。そのため、多数のプロフィール画像を読み込むと、メモリーリークが発生する可能性があります。利用側では、URL が不要になったときに URL.revokeObjectURL() を呼び出す必要があります。
  • 競合状態: 複数のユーザーを短時間に続けて取得すると、新しいリクエストが古いリクエストより先に完了する競合状態が発生する可能性があります。このフックはリクエストのキャンセルを実装していないため、古いデータが表示されることがあります。
  • エラー処理の粒度: このフックは現在のユーザーの取得に対して単一のエラー状態を提供しますが、一括操作における個々のユーザーの取得に対する詳細なエラー処理はありません。利用側で、一括操作に対する独自のエラー処理を実装する必要があります。
  • キャッシュの無効化: 古くなったユーザーデータを無効化する明示的な仕組みはありません。このフックは SWR 組み込みの再検証戦略に依存していますが、すべてのユースケースで十分とは限りません。
  • ユーザー操作の制限: このフックはユーザーデータの読み取りに重点を置いており、ユーザー情報を更新または変更するメソッドは提供していません。ユーザー管理機能が必要なアプリケーションでは、追加のフックまたは拡張機能が必要になります。
  • 空のユーザーID配列の処理: 空の配列で getBatchUserDetails を呼び出すと、空のオブジェクトを返し、リクエストを送信しません。呼び出し側では、フォールバック用のユーザーを期待するのではなく、空の結果を処理する必要があります。