Context API
Context provides a way to pass values through the component tree without prop drilling.
Overview
Sairin’s context system uses a stack-based approach, allowing for nested providers with automatic cleanup.
import { createContext, useContext } from 'sairin';
createContext
Create a new context with a default value.
function createContext<T>(defaultValue: T, name?: string): Context<T>
const themeContext = createContext('light');
const userContext = createContext<User | null>(null);
Context Interface
interface Context<T> {
defaultValue: T;
Provider: (props: { value: T; children?: any }) => () => void;
consume: () => T;
}
Provider
A component that provides a value to its children. Returns a cleanup function.
const cleanup = themeContext.Provider({ value: 'dark' });
// ... components can now read 'dark'
cleanup();
// ... back to default value
consume
Read the current value from the context.
const currentValue = themeContext.consume();
useContext
Hook to read a context value.
function useContext<T>(context: Context<T>): T
const theme = useContext(themeContext);
useContextProvider
Convenience function to set up a provider.
function useContextProvider<T>(
context: Context<T>,
value: T,
): () => void
const cleanup = useContextProvider(themeContext, 'dark');
Example
import { createContext, useContext, useContextProvider } from 'sairin';
const ThemeContext = createContext('light');
const UserContext = createContext<{ name: string } | null>(null);
function App() {
// Provide theme
const cleanupTheme = useContextProvider(ThemeContext, 'dark');
// Provide user
const cleanupUser = useContextProvider(UserContext, { name: 'Alice' });
// Clean up on unmount
return () => {
cleanupTheme();
cleanupUser();
};
}
function Header() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return `${user?.name} using ${theme} theme`;
}
Nested Providers
Contexts can be nested, and the inner provider shadows the outer one:
const ctx = createContext('root');
const cleanup1 = ctx.Provider({ value: 'outer' });
console.log(ctx.consume()); // 'outer'
const cleanup2 = ctx.Provider({ value: 'inner' });
console.log(ctx.consume()); // 'inner'
cleanup2();
console.log(ctx.consume()); // 'outer'
cleanup1();
console.log(ctx.consume()); // 'root'