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 3interface 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 4const getCurrentUserDetails = useCallback(async () => { const user: User = await getCurrent(client); return { "currentUser": user }; }, [client]);
Copied!1 2 3 4 5const 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 26const 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 5const 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 75import 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.