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.
useCallback
for memoizing functions, preventing unnecessary re-renders and optimizing performanceuseAdmin
structureCopied!1 2 3
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:
The useAdmin
hook implements several data fetching strategies:
Copied!1 2 3 4
const getCurrentUserDetails = useCallback(async () => { const user: User = await getCurrent(client); return { "currentUser": user }; }, [client]);
Copied!1 2 3 4 5
const getCurrentProfilePictureUrl = useCallback(async (user) => { const profilePictureResponse = await profilePicture(client, user.id); const blob = await profilePictureResponse.blob(); return URL.createObjectURL(blob); }, [client]);
getCurrentUserDetails
: Retrieves the currently authenticated user.getCurrentProfilePictureUrl
: Gets a blob URL for a user's profile picture.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.
The useAdmin
hook returns an object with the following structure:
Copied!1 2 3 4 5 6 7 8 9
{ 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 }
The useAdmin
hook implements a cache-first strategy for fetching user data:
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
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:
The useAdmin
implements a pattern for handling binary data (profile pictures) by converting API responses to blob URLs:
Copied!1 2 3 4 5
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:
The following external packages can be used with the useAdmin
hook.
Purpose: React hook for accessing the OSDK client instance Benefits:
Purpose: Foundry Admin SDK for interacting with user-related services. Benefits:
User
typegetCurrent
, profilePicture
, getBatch
)Purpose: Data fetching, caching, and state management library Benefits:
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
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> ); }
Consider the following scenarios and limitations before using the useAdmin
hook:
URL.createObjectURL()
but does not handle revoking these URLs. This could potentially lead to memory leaks if many profile pictures are loaded. Consumers should call URL.revokeObjectURL()
when the URLs are no longer needed.getBatchUserDetails
is 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.