Sharing State with Context in React
Learn how to share data throughout your application using both React's Context API and Anchor's Global Context.
What You'll Learn
In this tutorial, you'll learn:
- How to integrate React's Context API with Anchor for dependency injection.
- How to use Anchor's Global Context for dependency injection.
- When to use each approach for optimal state management.
Understanding Context Systems
Context systems allow you to share data throughout your application without prop drilling. Both Anchor and React provide powerful context mechanisms, and they can be used together to create flexible and scalable applications.
React's Context API
React's Context API is a built-in feature that allows you to share values between components without explicitly passing a prop through every level of the tree. This is the preferred method for dependency injection in most cases.
Anchor's Global Context
Anchor's Global Context is a reactive dependency injection system that allows you to store and retrieve values by key. It's particularly useful for sharing services, configurations, or global state throughout your application.
Anchor's global context is intended for easy context sharing without needing to create a specific context and wrap the component using the Context Provider. The constraint is, it's a global context which means the whole app shares the same place.
WARNING
Anchor's global context doesn't support NextJS server components at the moment. We will try to support it, but it's not a priority at the moment.
Using React's Context API
React's Context API is perfect for sharing values that don't change frequently or when you want to leverage React's built-in context system. When using React's context with Anchor, you pass the reactive state directly to the context, which means you don't need to manually update the context value - it automatically updates when the state changes.
import '@tailwindcss/browser';
import React, { createContext, useContext } from 'react';
import { observer, useAnchor } from '@anchorlib/react';
interface Settings {
theme: 'light' | 'dark';
language: 'en' | 'fr';
}
// Create React context
const AppSettings = createContext<Settings>({
theme: 'light',
language: 'en',
});
// Component that uses the context.
const ThemedButton = observer(() => {
// Get the current theme from the context.
const { theme } = useContext(AppSettings);
return (
<button
style={{
background: theme === 'dark' ? '#333' : '#fff',
color: theme === 'dark' ? '#fff' : '#000',
padding: '10px 20px',
border: '1px solid #ccc',
borderRadius: '4px',
}}>
Themed Button ({theme})
</button>
);
});
// Main app component
const App = () => {
const [settings] = useAnchor<Settings>({
theme: 'light',
language: 'en',
});
const toggleTheme = () => {
settings.theme = settings.theme === 'light' ? 'dark' : 'light';
};
return (
<div className="flex flex-col items-center justify-center gap-6 w-screen h-screen">
<h1>React Context Example</h1>
<p className="text-center px-10">
This example demonstrates how to use Anchor with React context to share state between components.
</p>
<AppSettings value={settings}>
<ThemedButton />
</AppSettings>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
};
export default App;Try it Yourself
In This Example
- We create a React context using
createContext() - We build a custom hook
useSettingsContext()for easier consumption - We use
useAnchorto manage the reactive state - We pass the reactive state directly to React's context provider using the
valueprop - Child components consume the context using
useContext()or our custom hook - When the Anchor state changes, React's context automatically updates
Using Anchor's Global Context
Anchor's Global Context provides a reactive Map that can be used to store and retrieve values by key. Let's see how to use it in a React application.
import '@tailwindcss/browser';
import { getContext, setContext } from '@anchorlib/core';
import { observer, useAnchor } from '@anchorlib/react';
import React from 'react';
interface Settings {
theme: 'light' | 'dark';
language: 'en' | 'fr';
}
// A component that consumes the Anchor context.
const ThemedButton = observer(() => {
// Get the current settings from Anchor context.
const settings = getContext<Settings>('settings', { theme: 'light', language: 'en' });
return (
<button
style={{
background: settings.theme === 'dark' ? '#333' : '#fff',
color: settings.theme === 'dark' ? '#fff' : '#000',
padding: '10px 20px',
border: '1px solid #ccc',
borderRadius: '4px',
}}>
Themed Button ({settings.theme})
</button>
);
});
// Main app component
const App = () => {
const [settings] = useAnchor<Settings>({
theme: 'light',
language: 'en',
});
setContext('settings', settings);
const toggleTheme = () => {
settings.theme = settings.theme === 'light' ? 'dark' : 'light';
};
return (
<div className="flex flex-col items-center justify-center gap-6 w-screen h-screen">
<h1>Anchor Context Example</h1>
<p className="text-center px-10">
This example demonstrates how to use Anchor's global context to share state between components.
</p>
<ThemedButton />
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
};
export default App;Try it Yourself
In This Example
- We use
useAnchorto create a reactive state object - We directly set values in the context using
setContext() - We retrieve values using
getContext() - The context values are reactive and will update when the Anchor state changes
- We don't need a provider component as with React's Context API
Combining Both Context Systems
You can combine both context systems to leverage the strengths of each. Use React's Context API for UI-related values and Anchor's Global Context for services and cross-cutting concerns.
import '@tailwindcss/browser';
import React, { createContext, useContext } from 'react';
import { getContext, setContext } from '@anchorlib/core';
import { observer, useAnchor } from '@anchorlib/react';
// Define our state interfaces
interface UIState {
theme: 'light' | 'dark';
sidebarOpen: boolean;
}
// React context for UI state
const UIContext = createContext<UIState>({
theme: 'light',
sidebarOpen: false,
});
// Service that might be shared across the app
class UserService {
getCurrentUser() {
return {
id: 1,
name: 'John Doe',
email: 'john@example.com',
};
}
}
// Custom hook for UI context
const useUIContext = () => {
return useContext(UIContext);
};
// Component using React context
const Header = observer(() => {
const { theme } = useUIContext();
return (
<header
className="w-full p-4 border-b"
style={{
background: theme === 'dark' ? '#333' : '#f5f5f5',
borderColor: theme === 'dark' ? '#555' : '#ddd',
}}>
<h1 className="text-xl font-bold">My App</h1>
</header>
);
});
// Component using Anchor context
const UserProfile = observer(() => {
// Get user service from Anchor context
const userService = getContext<UserService>(UserService.name);
const user = userService?.getCurrentUser();
if (!user) return <div>No user found</div>;
return (
<div className="p-4">
<h2 className="text-lg font-semibold">Welcome, {user.name}!</h2>
<p className="text-gray-600">Email: {user.email}</p>
</div>
);
});
// Main app component
const App = () => {
const [uiState] = useAnchor<UIState>({
theme: 'light',
sidebarOpen: false,
});
const toggleTheme = () => {
uiState.theme = uiState.theme === 'dark' ? 'light' : 'dark';
};
// Provide user service through Anchor context
const userService = new UserService();
setContext(UserService.name, userService);
const ThemeToggle = observer(() => {
return (
<button
onClick={toggleTheme}
className="px-4 py-2 rounded border"
style={{
background: uiState.theme === 'dark' ? '#444' : '#fff',
color: uiState.theme === 'dark' ? '#fff' : '#000',
borderColor: uiState.theme === 'dark' ? '#666' : '#ccc',
}}>
Switch to {uiState.theme === 'dark' ? 'Light' : 'Dark'} Mode
</button>
);
});
return (
<div className="flex flex-col min-h-screen">
<UIContext value={uiState}>
<Header />
<main className="flex-grow p-4">
<div className="max-w-2xl mx-auto">
<h1 className="text-2xl font-bold mb-6">Combined Context Example</h1>
<p className="mb-6">This example shows how to combine React's Context API with Anchor's Global Context.</p>
<UserProfile />
<div className="mt-6">
<ThemeToggle />
</div>
</div>
</main>
</UIContext>
</div>
);
};
export default App;Try it Yourself
In This Example
- We use React's Context API for UI-related state (theme, sidebar state)
- We use Anchor's Global Context for services (UserService)
- We pass reactive state directly to React's context provider using the
valueprop - We directly set services in Anchor's context using
setContext - Different components consume the appropriate context based on their needs
- Both context systems automatically update when the Anchor state changes
Key Points for Context Sharing
- Use React Context for UI state: Theme, user preferences, and other UI-related values
- Use Anchor Context for services: API clients, utilities, and cross-cutting concerns
- Combine both when needed: Leverage the strengths of each system
- Keep context values stable: Avoid creating new objects on every render
- Use custom hooks: Encapsulate context consumption logic in custom hooks for better reusability
- Pass reactive state to React context: When using React's context with Anchor, pass the reactive state directly to avoid manual updates