The useLearningTask hook is a custom React hook that provides functionality for fetching and managing learning task data in the advanced to-do application. It enriches basic task data with user information, media content, and metadata.
This hook uses patterns for handling different media types and binary content, showcasing error handling with Result types and resource management for blob URLs. It integrates seamlessly with the application's ontology while providing a clean interface for components.
View the useLearningTask reference code.
useCallback for memoizing functions to prevent unnecessary re-rendersuseAdminuseLearningTask structureCopied!1 2 3 4 5 6 7interface LearningTaskEnriched { osdkLearningTask: osdkLearningTask.OsdkInstance; mediaUrl: string; createdBy: User; assignedTo: User; mediaType: MediaType; }
This interface combines the SDK's learning task instance with user objects, a URL for the media content, and the detected media type to create a comprehensive data structure for components.
The useLearningTask hook uses SWR data fetching capabilities with a custom fetcher function that does the following:
fetchOneWithErrors for robust error handlingmediaReference exists, fetches binary content and creates a blob URLcontentUrl exists, uses it as an external linkLearningTaskEnriched structureCopied!1 2 3 4 5 6const fetcher = useCallback(async () => { const learningTaskResult = await client(osdkLearningTask).fetchOneWithErrors(task.osdkTask.$primaryKey as string); // Error handling and data processing // ... return learningTaskEnriched; }, [client, task.osdkTask.$primaryKey, getBatchUserDetails]);
The hook includes a helper function for determining media types from MIME types:
Copied!1 2 3 4 5 6 7 8const getMediaTypeFromMimeType = (mimeType: string): SupportedMediaType => { if (/application\/pdf/.test(mimeType)) { return SupportedMediaType.PDF; } else if (/image\/(jpeg|png|gif)/.test(mimeType)) { return SupportedMediaType.IMAGE; } // ...other type detection };
This utility uses regular expressions to match MIME types against common media formats, providing a consistent way to categorize different types of learning content.
The useLearningTask hook returns an object with the following structure:
Copied!1 2 3 4 5 6 7{ learningTask: LearningTaskEnriched | undefined; // The enriched learning task (undefined if not loaded) isLoading: boolean; // True during initial data loading isValidating: boolean; // True during background revalidation isError: any; // Error object if the request failed metadata: ObjectMetadata | undefined; // Metadata about the learning task object type }
The useLearningTask hook defines a string literal union type for different types of media:
Copied!1 2 3 4 5 6 7 8 9export const SupportedMediaType = { PDF: "PDF", IMAGE: "IMAGE", LINK: "LINK", VIDEO: "VIDEO", NONE: "NONE" } as const; export type SupportedMediaType = typeof SupportedMediaType[keyof typeof SupportedMediaType];
The hook uses the Result type from the OSDK for error handling:
Copied!1 2 3 4 5 6 7const learningTaskResult: Result<Osdk.Instance<osdkLearningTask>> = await client(osdkLearningTask).fetchOneWithErrors(task.osdkTask.$primaryKey as string); if (learningTaskResult.error) { throw new Error(learningTaskResult.error.message); } const learningTask = learningTaskResult.value;
This pattern does the following:
The hook manages blob URLs for media content:
Copied!1 2 3const response = await learningTask.mediaReference.fetchContents(); const blob: Blob | undefined = await response.blob(); const mediaUrl = blob ? URL.createObjectURL(blob) : "";
This pattern does the following:
The following external packages can be used with the useLearningTask hook.
Purpose: Data fetching, caching, and state management Benefits:
Purpose: Ontology SDK client for interacting with a backend data service Benefits:
useOsdkClient for accessing the client instancePurpose: Application-specific SDK with predefined data types Benefits:
osdkLearningTask)Purpose: Utility library for common operations Benefits:
_.uniq and _.compact for array manipulationCopied!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 77 78 79 80 81 82 83 84 85 86 87import useLearningTask, { SupportedMediaType } from '../dataServices/useLearningTask'; function LearningTaskViewer({ task }) { const { learningTask, isLoading, isError, metadata } = useLearningTask(task); if (isLoading) return <div>Loading learning materials...</div>; if (isError) return <div>Error loading content: {isError.message}</div>; if (!learningTask) return <div>No learning content available</div>; // Render different media types appropriately const renderMedia = () => { switch (learningTask.mediaType) { case SupportedMediaType.PDF: return ( <div className="pdf-viewer"> <iframe src={`${learningTask.mediaUrl}#toolbar=0`} title="PDF Content" width="100%" height="500px" /> </div> ); case SupportedMediaType.IMAGE: return ( <div className="image-viewer"> <img src={learningTask.mediaUrl} alt="Learning content" className="learning-image" /> </div> ); case SupportedMediaType.VIDEO: return ( <div className="video-player"> <video src={learningTask.mediaUrl} controls width="100%" /> </div> ); case SupportedMediaType.LINK: return ( <div className="external-link"> <a href={learningTask.mediaUrl} target="_blank" rel="noopener noreferrer" > Open learning resource </a> </div> ); default: return <div>No media content available</div>; } }; return ( <div className="learning-task-viewer"> <h2>{learningTask.osdkLearningTask.title}</h2> <div className="content-section"> <div className="content-type-badge">{learningTask.mediaType}</div> {renderMedia()} </div> <div className="description-section"> <h3>Description</h3> <p>{learningTask.osdkLearningTask.description || "No description provided"}</p> </div> <div className="people-section"> <div className="assigned-to"> <span>Assigned to: </span> <span>{learningTask.assignedTo?.displayName || "Unassigned"}</span> </div> <div className="created-by"> <span>Created by: </span> <span>{learningTask.createdBy?.displayName || "Unknown"}</span> </div> </div> </div> ); }
Consider the following scenarios and limitations before using the useLearningTask hook:
URL.revokeObjectURL() when they are no longer needed to prevent memory leaks.NONE, which might not be appropriate for some specialized media formats.