useAdmin hook(useAdmin 钩子(useAdmin hook))¶
The useAdmin hook is a custom React hook that provides functionality for interacting with user data from the Foundry Admin API. It centralizes user-related operations such as retrieving the current authenticated user, fetching multiple users in batch, and managing profile pictures.
This hook leverages SWR (stale-while-revalidate) for efficient data fetching and caching strategies, which helps optimize network requests and provides a consistent interface for user data across your application. By combining SWR with the Foundry Admin SDK, it creates a robust solution for user management that handles loading states, caching, and error handling automatically.
View the useAdmin reference code.
Key functions¶
- Uses React's
useCallbackfor memoizing functions, preventing unnecessary re-renders and optimizing performance - Implements an intelligent caching strategy with SWR to minimize API calls and improve application responsiveness
- Creates blob URLs for profile pictures, allowing efficient rendering of binary image data
- Handles empty user ID arrays gracefully by returning the current user as a fallback
- Supports batch fetching of users to reduce the number of API calls when multiple users need to be loaded
- Provides loading, validation, and error states for comprehensive UI feedback
- Uses TypeScript for strong typing of all data structures and function signatures
useAdmin structure¶
Interface definition¶
interface UserDetails {
[key: string]: User;
}
This interface defines a dictionary-type structure that maps string keys (user IDs) to User objects. This pattern enables the following:
- Fast O(1) lookups of users by ID
- Efficient storage of multiple user objects
- Easy aggregation of user data from multiple sources (cache and API)
Data fetching¶
The useAdmin hook implements several data fetching strategies:
- Current user fetching:
const getCurrentUserDetails = useCallback(async () => {
const user: User = await getCurrent(client);
return { "currentUser": user };
}, [client]);
- Profile picture fetching:
const getCurrentProfilePictureUrl = useCallback(async (user) => {
const profilePictureResponse = await profilePicture(client, user.id);
const blob = await profilePictureResponse.blob();
return URL.createObjectURL(blob);
}, [client]);
- Batch user fetching with cache optimization.
CRUD operations¶
- Read (current user):
getCurrentUserDetails: Retrieves the currently authenticated user.- The primary SWR hook fetches and maintains current user state.
- Read (profile picture):
getCurrentProfilePictureUrl: Gets a blob URL for a user's profile picture.- Read (multiple users):
getBatchUserDetails: Efficiently fetches multiple users with cache optimization.
Note that the useAdmin hook focuses on read operations; it does not implement create, update, or delete operations for user data.
Return value¶
The useAdmin hook returns an object with the following structure:
{
users: UserDetails | undefined; // Object containing all fetched users
currentUser: User | undefined; // The currently logged-in user
isLoading: boolean; // Loading state for data fetching
isValidating: boolean; // Whether data is being revalidated
isError: Error | undefined; // Error state if any
getBatchUserDetails: (userIds: string[]) => Promise<UserDetails>; // Function to fetch multiple users
getCurrentProfilePictureUrl: (user: User) => Promise<string>; // Function to get a user's profile picture URL
}
Implementation¶
Cache-first data fetching strategy¶
The useAdmin hook implements a cache-first strategy for fetching user data:
const getBatchUserDetails = useCallback(async (userIds) => {
const cachedUser: UserDetails = {};
const usersToFetch: string[] = [];
// Check cache first for each requested user
userIds.forEach((userId) => {
const cachedUserState = cache.get(`user-${userId}`);
if (cachedUserState && cachedUserState.data) {
cachedUser[userId] = cachedUserState.data as User;
} else {
usersToFetch.push(userId);
}
});
// Only fetch users not found in cache
if (usersToFetch.length > 0) {
const usersRequest = await getBatch(client, usersToFetch.map((userId) => ({ userId })));
// Store results in cache for future use
Object.entries(usersRequest.data).forEach(([userId, user]) => {
cachedUser[userId] = user;
mutate(`user-${userId}`, user, { revalidate: false });
});
}
return cachedUser;
}, [cache, client, getCurrentUserDetails]);
This strategy improves performance with the following steps:
- Checking if requested data exists in cache
- Only fetching data that is not already available
- Updating the cache with newly fetched data
- Returning a combination of cached and freshly fetched data
Blob URL creation pattern¶
The useAdmin implements a pattern for handling binary data (profile pictures) by converting API responses to blob URLs:
const getCurrentProfilePictureUrl = useCallback(async (user) => {
const profilePictureResponse = await profilePicture(client, user.id);
const blob = await profilePictureResponse.blob();
return URL.createObjectURL(blob);
}, [client]);
This pattern does the following:
- Fetches binary data from the API
- Converts the response to a blob object
- Creates a temporary URL that can be used as an image source
- Allows for efficient rendering of binary data in the UI
External packages¶
The following external packages can be used with the useAdmin hook.
@osdk/react (useOsdkClient)¶
Purpose: React hook for accessing the OSDK client instance Benefits:
- Provides access to the authenticated client needed for all API calls
- Manages authentication context without requiring manual prop drilling
- Ensures consistent API access across the application
- Integrates with Foundry's authentication mechanisms
@osdk/foundry.admin¶
Purpose: Foundry Admin SDK for interacting with user-related services. Benefits:
- Provides type-safe interfaces for user data with the
Usertype - Includes specialized functions for user operations (
getCurrent,profilePicture,getBatch) - Abstracts away complex API interactions into simple function calls
- Ensures compatibility with Foundry's backend services and data models
SWR: useSWR, mutate, useSWRConfig¶
Purpose: Data fetching, caching, and state management library Benefits:
- Provides an elegant way to fetch, cache, and revalidate data with a simple API
- Manages loading, error, and validation states automatically without additional code
- Implements optimistic UI updates and background revalidation strategies Offers a global cache that can be accessed and manipulated throughout the application
- Reduces unnecessary network requests through intelligent caching
Usage example¶
import useAdmin from '../dataServices/useAdmin';
import { useState, useEffect } from 'react';
// Example 1: Display current user information with profile picture
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.displayName}</h1>
<p>Email: {currentUser.email}</p>
<p>User ID: {currentUser.id}</p>
</div>
);
}
// Example 2: Display a list of collaborators
function CollaboratorsList({ userIds }) {
const { getBatchUserDetails, isLoading } = useAdmin();
const [users, setUsers] = useState({});
const [error, setError] = useState(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.displayName}</span>
<span className="email">{user.email}</span>
</li>
))}
</ul>
</div>
);
}
Edge cases and limitations¶
Consider the following scenarios and limitations before using the useAdmin hook:
- Profile picture memory management: The hook creates blob URLs for profile pictures using
URL.createObjectURL()but does not handle revoking these URLs. This could potentially lead to memory leaks if many profile pictures are loaded. Consumers should callURL.revokeObjectURL()when the URLs are no longer needed. - Race conditions: When fetching multiple users in quick succession, there is potential for race conditions where newer requests complete before older ones. The hook does not implement request cancellation, so stale data might occasionally be displayed.
- Error handling granularity: The hook provides a single error state for the current user fetch but does not have detailed error handling for individual user fetches in the batch operation. Consumers need to implement their own error handling for batch operations.
- Cache invalidation: There is no explicit mechanism for invalidating stale user data. The hook relies on SWR's built-in revalidation strategies, which might not be sufficient for all use cases.
- Limited user operations: The hook focuses on reading user data but does not provide methods for updating or modifying user information. Applications requiring user management features would need additional hooks or extensions.
- Empty user ID handling: When
getBatchUserDetailsis called with an empty array, it returns the current user with an empty string key. While this prevents errors, it might not be the expected behavior in all contexts.
中文翻译¶
useAdmin 钩子(useAdmin hook)¶
useAdmin 是一个自定义 React 钩子,用于与 Foundry Admin API 中的用户数据进行交互。它集中管理用户相关操作,例如获取当前已认证用户、批量获取多个用户以及管理个人资料图片。
该钩子利用 SWR(stale-while-revalidate)实现高效的数据获取和缓存策略,有助于优化网络请求,并为应用程序中的用户数据提供一致的接口。通过将 SWR 与 Foundry Admin SDK 结合使用,它创建了一个强大的用户管理解决方案,能够自动处理加载状态、缓存和错误处理。
主要功能¶
- 使用 React 的
useCallback对函数进行记忆化,防止不必要的重新渲染并优化性能 - 通过 SWR 实现智能缓存策略,减少 API 调用次数并提高应用响应速度
- 为个人资料图片创建 blob URL,实现二进制图像数据的高效渲染
- 优雅地处理空用户 ID 数组,默认返回当前用户作为后备方案
- 支持批量获取用户,减少需要加载多个用户时的 API 调用次数
- 提供加载、验证和错误状态,实现全面的 UI 反馈
- 使用 TypeScript 对所有数据结构和函数签名进行强类型定义
useAdmin 结构¶
接口定义¶
interface UserDetails {
[key: string]: User;
}
该接口定义了一个字典类型结构,将字符串键(用户 ID)映射到 User 对象。这种模式支持以下功能:
- 通过 ID 实现 O(1) 级别的快速用户查找
- 高效存储多个用户对象
- 轻松聚合来自多个来源(缓存和 API)的用户数据
数据获取¶
useAdmin 钩子实现了多种数据获取策略:
- 当前用户获取:
const getCurrentUserDetails = useCallback(async () => {
const user: User = await getCurrent(client);
return { "currentUser": user };
}, [client]);
- 个人资料图片获取:
const getCurrentProfilePictureUrl = useCallback(async (user) => {
const profilePictureResponse = await profilePicture(client, user.id);
const blob = await profilePictureResponse.blob();
return URL.createObjectURL(blob);
}, [client]);
- 带缓存优化的批量用户获取。
CRUD 操作¶
- 读取(当前用户):
getCurrentUserDetails:获取当前已认证用户。- 主要的 SWR 钩子负责获取和维护当前用户状态。
- 读取(个人资料图片):
getCurrentProfilePictureUrl:获取用户个人资料图片的 blob URL。- 读取(多个用户):
getBatchUserDetails:通过缓存优化高效获取多个用户。
请注意,useAdmin 钩子专注于读取操作;它不实现用户数据的创建、更新或删除操作。
返回值¶
useAdmin 钩子返回一个具有以下结构的对象:
{
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 钩子实现了缓存优先的用户数据获取策略:
const getBatchUserDetails = useCallback(async (userIds) => {
const cachedUser: UserDetails = {};
const usersToFetch: string[] = [];
// 首先检查每个请求用户的缓存
userIds.forEach((userId) => {
const cachedUserState = cache.get(`user-${userId}`);
if (cachedUserState && cachedUserState.data) {
cachedUser[userId] = cachedUserState.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]) => {
cachedUser[userId] = user;
mutate(`user-${userId}`, user, { revalidate: false });
});
}
return cachedUser;
}, [cache, client, getCurrentUserDetails]);
该策略通过以下步骤提高性能:
- 检查请求的数据是否存在于缓存中
- 仅获取尚未可用的数据
- 使用新获取的数据更新缓存
- 返回缓存数据和最新获取数据的组合
Blob URL 创建模式¶
useAdmin 实现了一种处理二进制数据(个人资料图片)的模式,将 API 响应转换为 blob URL:
const getCurrentProfilePictureUrl = useCallback(async (user) => {
const profilePictureResponse = await profilePicture(client, user.id);
const blob = await profilePictureResponse.blob();
return URL.createObjectURL(blob);
}, [client]);
该模式执行以下操作:
- 从 API 获取二进制数据
- 将响应转换为 blob 对象
- 创建一个可用作图像源的临时 URL
- 允许在 UI 中高效渲染二进制数据
外部包¶
以下外部包可与 useAdmin 钩子一起使用。
@osdk/react (useOsdkClient)¶
用途: 用于访问 OSDK 客户端实例的 React 钩子 优势:
- 提供所有 API 调用所需的已认证客户端访问权限
- 管理认证上下文,无需手动进行属性逐层传递
- 确保整个应用程序中的 API 访问一致性
- 与 Foundry 的认证机制集成
@osdk/foundry.admin¶
用途: 用于与用户相关服务交互的 Foundry Admin SDK 优势:
- 通过
User类型提供用户数据的类型安全接口 - 包含用于用户操作的专业函数(
getCurrent、profilePicture、getBatch) - 将复杂的 API 交互抽象为简单的函数调用
- 确保与 Foundry 后端服务和数据模型的兼容性
SWR: useSWR, mutate, useSWRConfig¶
用途: 数据获取、缓存和状态管理库 优势:
- 通过简单的 API 提供优雅的数据获取、缓存和重新验证方式
- 自动管理加载、错误和验证状态,无需额外代码
- 实现乐观 UI 更新和后台重新验证策略
- 提供可在整个应用程序中访问和操作的全局缓存
- 通过智能缓存减少不必要的网络请求
使用示例¶
import useAdmin from '../dataServices/useAdmin';
import { useState, useEffect } from 'react';
// 示例 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("加载个人资料图片失败", error);
}
}
}
loadProfilePic();
}, [currentUser, getCurrentProfilePictureUrl]);
if (isLoading) return <div>正在加载用户信息...</div>;
if (isError) return <div>加载用户信息时出错</div>;
if (!currentUser) return <div>未找到用户</div>;
return (
<div className="user-profile">
{profilePicUrl && <img src={profilePicUrl} alt="个人资料" className="profile-pic" />}
<h1>{currentUser.displayName}</h1>
<p>邮箱: {currentUser.email}</p>
<p>用户 ID: {currentUser.id}</p>
</div>
);
}
// 示例 2:显示协作者列表
function CollaboratorsList({ userIds }) {
const { getBatchUserDetails, isLoading } = useAdmin();
const [users, setUsers] = useState({});
const [error, setError] = useState(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>正在加载协作者...</div>;
if (error) return <div>加载协作者时出错</div>;
return (
<div className="collaborators">
<h2>项目协作者</h2>
<ul>
{Object.entries(users).map(([userId, user]) => (
<li key={userId} className="collaborator-item">
<span className="name">{user.displayName}</span>
<span className="email">{user.email}</span>
</li>
))}
</ul>
</div>
);
}
边界情况和限制¶
在使用 useAdmin 钩子之前,请考虑以下场景和限制:
- 个人资料图片内存管理: 该钩子使用
URL.createObjectURL()创建个人资料图片的 blob URL,但不会处理这些 URL 的撤销。如果加载大量个人资料图片,这可能导致内存泄漏。消费者应在不再需要这些 URL 时调用URL.revokeObjectURL()。 - 竞态条件: 当快速连续获取多个用户时,可能出现较新请求在较旧请求之前完成的竞态条件。该钩子未实现请求取消功能,因此偶尔可能会显示过时数据。
- 错误处理粒度: 该钩子为当前用户获取提供单一错误状态,但未对批量操作中的单个用户获取提供详细的错误处理。消费者需要为批量操作实现自己的错误处理。
- 缓存失效: 没有明确的机制用于使过时的用户数据失效。该钩子依赖 SWR 内置的重新验证策略,这可能不足以满足所有使用场景。
- 有限的用户操作: 该钩子专注于读取用户数据,但未提供更新或修改用户信息的方法。需要用户管理功能的应用程序需要额外的钩子或扩展。
- 空用户 ID 处理: 当使用空数组调用
getBatchUserDetails时,它会返回带有空字符串键的当前用户。虽然这可以防止错误,但在某些上下文中可能不是预期的行为。