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-rendersuseAdmin
useLearningTask
structureCopied!1 2 3 4 5 6 7
interface 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 6
const 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 8
const getMediaTypeFromMimeType = (mimeType: string): SupportedMediaType => { if (/application\/pdf/.test(mimeType)) { return SupportedMediaType.PDF; } else if (/docs/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 9
export 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 7
const 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 3
const 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 87
import 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.