- Spec files
npm-react
Describes: pkg:npm/react@18.3.x
- Description
- React is a JavaScript library for building user interfaces with declarative, component-based architecture.
- Author
- tessl
- Last updated
context.md docs/
1# Context API23React Context provides a way to share data across the component tree without passing props through every level. It's ideal for global state like themes, authentication, or user preferences.45## Capabilities67### createContext89Creates a new React context object with Provider and Consumer components.1011```javascript { .api }12/**13* Creates a React context for sharing data across component tree14* @param defaultValue - Default value when no Provider is found15* @returns Context object with Provider and Consumer16*/17function createContext<T>(defaultValue: T): Context<T>;1819interface Context<T> {20Provider: ExoticComponent<ProviderProps<T>>;21Consumer: ExoticComponent<ConsumerProps<T>>;22displayName?: string;23}2425interface ProviderProps<T> {26value: T;27children?: ReactNode;28}2930interface ConsumerProps<T> {31children: (value: T) => ReactNode;32}33```3435**Usage Examples:**3637```javascript38import React, { createContext, useContext, useState } from 'react';3940// Create theme context41const ThemeContext = createContext('light');4243function App() {44const [theme, setTheme] = useState('light');4546return (47<ThemeContext.Provider value={theme}>48<Header />49<MainContent />50<ThemeToggle onToggle={() => setTheme(theme === 'light' ? 'dark' : 'light')} />51</ThemeContext.Provider>52);53}5455function Header() {56const theme = useContext(ThemeContext);5758return (59<header className={`header-${theme}`}>60<h1>My App</h1>61</header>62);63}6465// Complex context with multiple values66const UserContext = createContext({67user: null,68login: () => {},69logout: () => {},70isAuthenticated: false71});7273function UserProvider({ children }) {74const [user, setUser] = useState(null);7576const login = async (credentials) => {77const userData = await authenticateUser(credentials);78setUser(userData);79};8081const logout = () => {82setUser(null);83};8485const value = {86user,87login,88logout,89isAuthenticated: !!user90};9192return (93<UserContext.Provider value={value}>94{children}95</UserContext.Provider>96);97}9899// Custom hook for using context100function useUser() {101const context = useContext(UserContext);102if (!context) {103throw new Error('useUser must be used within UserProvider');104}105return context;106}107108// Component using the context109function UserProfile() {110const { user, logout, isAuthenticated } = useUser();111112if (!isAuthenticated) {113return <div>Please log in</div>;114}115116return (117<div>118<h2>Welcome, {user.name}</h2>119<button onClick={logout}>Logout</button>120</div>121);122}123```124125### Consumer Pattern126127Using Context.Consumer for consuming context values (alternative to useContext hook).128129```javascript { .api }130/**131* Context Consumer component for accessing context values132* @param children - Render function that receives context value133*/134interface ConsumerProps<T> {135children: (value: T) => ReactNode;136}137```138139**Usage Examples:**140141```javascript142import React, { createContext } from 'react';143144const ConfigContext = createContext({145apiUrl: 'https://api.example.com',146timeout: 5000,147retries: 3148});149150// Using Consumer component151function ApiStatus() {152return (153<ConfigContext.Consumer>154{(config) => (155<div>156<p>API URL: {config.apiUrl}</p>157<p>Timeout: {config.timeout}ms</p>158<p>Retries: {config.retries}</p>159</div>160)}161</ConfigContext.Consumer>162);163}164165// Multiple contexts with Consumer166function UserDisplay() {167return (168<ThemeContext.Consumer>169{(theme) => (170<UserContext.Consumer>171{(user) => (172<div className={`user-display theme-${theme}`}>173<h3>{user.name}</h3>174<p>{user.email}</p>175</div>176)}177</UserContext.Consumer>178)}179</ThemeContext.Consumer>180);181}182183// Render prop pattern with Consumer184function withConfig(Component) {185return function ConfiguredComponent(props) {186return (187<ConfigContext.Consumer>188{(config) => <Component {...props} config={config} />}189</ConfigContext.Consumer>190);191};192}193194const ConfiguredApiClient = withConfig(ApiClient);195```196197### Context with Reducers198199Complex state management using context with useReducer.200201```javascript { .api }202/**203* Context pattern with reducer for complex state management204*/205// No specific API - this is a pattern using createContext with useReducer206```207208**Usage Examples:**209210```javascript211import React, { createContext, useContext, useReducer } from 'react';212213// Shopping cart context with reducer214const CartContext = createContext();215216const cartReducer = (state, action) => {217switch (action.type) {218case 'ADD_ITEM':219const existingItem = state.items.find(item => item.id === action.payload.id);220if (existingItem) {221return {222...state,223items: state.items.map(item =>224item.id === action.payload.id225? { ...item, quantity: item.quantity + 1 }226: item227)228};229}230return {231...state,232items: [...state.items, { ...action.payload, quantity: 1 }]233};234235case 'REMOVE_ITEM':236return {237...state,238items: state.items.filter(item => item.id !== action.payload)239};240241case 'UPDATE_QUANTITY':242return {243...state,244items: state.items.map(item =>245item.id === action.payload.id246? { ...item, quantity: action.payload.quantity }247: item248)249};250251case 'CLEAR_CART':252return { ...state, items: [] };253254default:255return state;256}257};258259function CartProvider({ children }) {260const [state, dispatch] = useReducer(cartReducer, {261items: [],262isOpen: false263});264265const addItem = (product) => {266dispatch({ type: 'ADD_ITEM', payload: product });267};268269const removeItem = (productId) => {270dispatch({ type: 'REMOVE_ITEM', payload: productId });271};272273const updateQuantity = (productId, quantity) => {274dispatch({ type: 'UPDATE_QUANTITY', payload: { id: productId, quantity } });275};276277const clearCart = () => {278dispatch({ type: 'CLEAR_CART' });279};280281const getTotalItems = () => {282return state.items.reduce((total, item) => total + item.quantity, 0);283};284285const getTotalPrice = () => {286return state.items.reduce((total, item) => total + (item.price * item.quantity), 0);287};288289const value = {290items: state.items,291addItem,292removeItem,293updateQuantity,294clearCart,295getTotalItems,296getTotalPrice297};298299return (300<CartContext.Provider value={value}>301{children}302</CartContext.Provider>303);304}305306function useCart() {307const context = useContext(CartContext);308if (!context) {309throw new Error('useCart must be used within CartProvider');310}311return context;312}313314// Usage in components315function ProductCard({ product }) {316const { addItem } = useCart();317318return (319<div className="product-card">320<h3>{product.name}</h3>321<p>${product.price}</p>322<button onClick={() => addItem(product)}>323Add to Cart324</button>325</div>326);327}328329function CartSummary() {330const { items, getTotalItems, getTotalPrice, clearCart } = useCart();331332return (333<div className="cart-summary">334<h3>Cart ({getTotalItems()} items)</h3>335<p>Total: ${getTotalPrice().toFixed(2)}</p>336{items.length > 0 && (337<button onClick={clearCart}>Clear Cart</button>338)}339</div>340);341}342```343344### createServerContext345346Creates server-specific context for server-side rendering scenarios.347348```javascript { .api }349/**350* Creates server-specific context for SSR351* @param globalName - Global identifier for the context352* @param defaultValue - Default context value353* @returns Server context object354*/355function createServerContext<T>(globalName: string, defaultValue: T): ServerContext<T>;356357interface ServerContext<T> {358Provider: ExoticComponent<ProviderProps<T>>;359Consumer: ExoticComponent<ConsumerProps<T>>;360displayName?: string;361}362```363364**Usage Examples:**365366```javascript367import React, { createServerContext, useContext } from 'react';368369// Server-specific context for request data370const RequestContext = createServerContext('RequestContext', {371url: '',372method: 'GET',373headers: {},374userAgent: ''375});376377// Server-side provider378function ServerApp({ request, children }) {379const requestData = {380url: request.url,381method: request.method,382headers: request.headers,383userAgent: request.get('User-Agent') || ''384};385386return (387<RequestContext.Provider value={requestData}>388{children}389</RequestContext.Provider>390);391}392393// Component that uses server context394function RequestInfo() {395const request = useContext(RequestContext);396397return (398<div>399<p>URL: {request.url}</p>400<p>Method: {request.method}</p>401<p>User Agent: {request.userAgent}</p>402</div>403);404}405406// Server context for localization407const LocaleContext = createServerContext('LocaleContext', {408locale: 'en',409currency: 'USD',410timezone: 'UTC'411});412413function LocalizedApp({ locale, children }) {414const localeData = {415locale: locale || 'en',416currency: getCurrencyForLocale(locale),417timezone: getTimezoneForLocale(locale)418};419420return (421<LocaleContext.Provider value={localeData}>422{children}423</LocaleContext.Provider>424);425}426427function PriceDisplay({ amount }) {428const { currency, locale } = useContext(LocaleContext);429430const formattedPrice = new Intl.NumberFormat(locale, {431style: 'currency',432currency: currency433}).format(amount);434435return <span>{formattedPrice}</span>;436}437```438439### Context Best Practices440441Common patterns and best practices for using React Context effectively.442443**Usage Examples:**444445```javascript446// 1. Separate contexts for different concerns447const AuthContext = createContext();448const ThemeContext = createContext();449const SettingsContext = createContext();450451// 2. Custom hooks with error boundaries452function useAuth() {453const context = useContext(AuthContext);454if (context === undefined) {455throw new Error('useAuth must be used within an AuthProvider');456}457return context;458}459460// 3. Context composition for multiple providers461function AppProviders({ children }) {462return (463<AuthProvider>464<ThemeProvider>465<SettingsProvider>466<I18nProvider>467{children}468</I18nProvider>469</SettingsProvider>470</ThemeProvider>471</AuthProvider>472);473}474475// 4. Memoized context values to prevent unnecessary re-renders476function ThemeProvider({ children }) {477const [theme, setTheme] = useState('light');478479const value = useMemo(480() => ({481theme,482setTheme,483toggleTheme: () => setTheme(prev => prev === 'light' ? 'dark' : 'light')484}),485[theme]486);487488return (489<ThemeContext.Provider value={value}>490{children}491</ThemeContext.Provider>492);493}494495// 5. Split context to avoid unnecessary re-renders496const StateContext = createContext();497const DispatchContext = createContext();498499function StateProvider({ children }) {500const [state, dispatch] = useReducer(reducer, initialState);501502return (503<StateContext.Provider value={state}>504<DispatchContext.Provider value={dispatch}>505{children}506</DispatchContext.Provider>507</StateContext.Provider>508);509}510511function useState() {512const context = useContext(StateContext);513if (!context) {514throw new Error('useState must be used within StateProvider');515}516return context;517}518519function useDispatch() {520const context = useContext(DispatchContext);521if (!context) {522throw new Error('useDispatch must be used within StateProvider');523}524return context;525}526```527528## Types529530```javascript { .api }531// Context types532interface Context<T> {533Provider: ExoticComponent<ProviderProps<T>>;534Consumer: ExoticComponent<ConsumerProps<T>>;535displayName?: string;536}537538interface ServerContext<T> extends Context<T> {}539540interface ProviderProps<T> {541value: T;542children?: ReactNode;543}544545interface ConsumerProps<T> {546children: (value: T) => ReactNode;547}548549// Context hook type550function useContext<T>(context: Context<T>): T;551```