6.2 KiB
Executable File
Application Contexts
Last Updated: November 30, 2025
This project uses React Context to manage global state across the application. Two main contexts are provided:
UserProvider- Authentication state and user informationGlossaryProvider- Glossary data with word lookup functionality
Both providers are configured in the root layout and available throughout the application.
Table of Contents
userContext
The userContext manages the user's authentication state and user information. It provides authentication status, user details, and logout functionality.
Features
- Tracks authentication state with
isLoggedInboolean - Provides complete user object with profile information
- Offers
logoutfunction for ending sessions - Automatically fetches session on mount
- Handles loading states during session checks
Available Hooks
The userContext provides multiple hooks for accessing different parts of the state:
import {
useUser, // Full user object and logout function
useIsLoggedIn, // Only authentication status boolean
} from "@/context/user.context";
// In component:
const { user, logout } = useUser(); // { user: {...}, logout: fn }
const isLoggedIn = useIsLoggedIn(); // boolean
User Object Structure
When authenticated, the user object contains (example):
{
id: 1,
username: "student123",
isAdmin: false
// Additional fields from the users table
}
When not authenticated, user is null.
Usage Examples
Conditional Rendering Based on Authentication
import { useIsLoggedIn } from "@/context/user.context";
export default function Page() {
const isLoggedIn = useIsLoggedIn();
return (
<div>
{/* Public content */}
<h1>Welcome</h1>
{/* Content for authenticated users */}
{isLoggedIn && (
<div>
<p>Content visible to logged-in users only</p>
</div>
)}
{/* Content for non-authenticated users */}
{!isLoggedIn && (
<div>
<p>Please log in to access exercises</p>
</div>
)}
</div>
);
}
Using User Information and Logout
import { useUser } from "@/context/user.context";
export default function ProfilePage() {
const { user, logout } = useUser();
if (!user) {
return <p>Please log in</p>;
}
const handleLogout = async () => {
await logout();
// User is logged out, context updates automatically
};
return (
<div>
<h1>Profile</h1>
<p>Username: {user.username}</p>
{user.isAdmin && <p>Admin privileges active</p>}
<button onClick={handleLogout}>Log out</button>
</div>
);
}
Use Cases
- Logged-In Users: Display protected content like exercises, profile, admin panels
- Logged-Out Users: Show login prompts, public content, registration links
- Admin Users: Conditionally render admin-only features using
user.isAdmin
Notes
- The
UserProvideris configured in the root layout (app/layout.js), so the context is available app-wide. - The context fetches session data from
/api/auth/sessionand calls/api/auth/logoutto end sessions. - Session checks run on mount; prefer hooks (
useUser,useIsLoggedIn) to access the state.
glossaryContext
The glossaryContext provides access to the application's glossary data, including word definitions and lookup functionality.
Features
- Fetches and caches glossary entries on mount
- Provides word lookup by entry or inflected form
- Handles loading and error states
- Exposes raw entries array and optimized word map
Available Hook
import { useGlossary } from "@/context/glossary.context";
// In component:
const { entries, wordMap, isLoading, error } = useGlossary();
Returns:
entries- Array of all glossary entrieswordMap- Map for fast lookup:{ word: entryId }isLoading- Boolean indicating if data is being fetchederror- Error object if fetch failed, null otherwise
Usage Examples
Basic Glossary Display
import { useGlossary } from "@/context/glossary.context";
export default function GlossaryPage() {
const { entries, isLoading, error } = useGlossary();
if (isLoading) return <p>Loading glossary...</p>;
if (error) return <p>Failed to load glossary</p>;
return (
<ul>
{entries.map((entry) => (
<li key={entry.id}>
<strong>{entry.word}</strong>: {entry.definition}
</li>
))}
</ul>
);
}
Notes
- The
GlossaryProvideris configured in the root layout and available throughout the application. - Glossary data is fetched from
/api/glossaryon mount. - The
wordMapenables O(1) lookup for both base forms and inflected forms of words. - Use
isLoadingto show loading states and prevent rendering before data is available.
Testing with Contexts
When testing components that use contexts, wrap them in the appropriate provider:
import { render } from "@testing-library/react";
import { UserProvider } from "@/context/user.context";
import { GlossaryProvider } from "@/context/glossary.context";
import MyComponent from "./MyComponent";
test("renders with context", () => {
render(
<UserProvider>
<GlossaryProvider>
<MyComponent />
</GlossaryProvider>
</UserProvider>
);
// Test assertions...
});
Mocking Context Values
For unit tests, mock the context hooks:
import { vi } from "vitest";
import * as userContext from "@/context/user.context";
vi.spyOn(userContext, "useUser").mockReturnValue({
user: { id: 1, username: "testuser", isAdmin: false },
logout: vi.fn(),
});
vi.spyOn(userContext, "useIsLoggedIn").mockReturnValue(true);
This approach isolates component logic from context implementation.