Working with Context API
1. Creating Context
To create a context in React Native, use the React.createContext API. This function returns a context object that includes a Provider component and a Consumer component.
| Property | Description | Example |
|---|---|---|
| React.createContext(defaultValue) | Creates a new context with an optional default value. | const ThemeContext = React.createContext('light'); |
Example: Creating a Theme Context
import React from 'react';
const ThemeContext = React.createContext('light');
export default ThemeContext;
2. Providing Context Values
Example: Basic Context Provider
// Create a context
const ThemeContext = React.createContext();
// Provide context values
function App() {
return (
<ThemeContext.Provider value={{ theme: 'dark', toggleTheme: () => {} }}>
<Toolbar />
</ThemeContext.Provider>
);
}
| Property | Description | Usage |
|---|---|---|
| value | The value to be passed to consumers | Required. Can be any type (object, string, function) |
Note: The
value prop is the only required prop for Context.Provider. It can be any JavaScript value, including objects or functions.
Warning: If you don't provide a value, all consumers will receive
undefined.
Example: Context with State
const ThemeContext = React.createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
3. Consuming Context
Example: Basic Context Consumption
import React, { useContext } from 'react';
const ThemeContext = React.createContext();
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.text }}>
Themed Button
</button>
);
}
function App() {
const theme = {
background: '#333',
text: '#fff'
};
return (
<ThemeContext.Provider value={theme}>
<ThemedButton />
</ThemeContext.Provider>
);
}
| Property | Description | Usage |
|---|---|---|
| useContext | Hook to consume context values | const value = useContext(Context) |
Note:
useContext will re-render the component whenever the context value changes.
Example: Multiple Contexts
const ThemeContext = React.createContext();
const UserContext = React.createContext();
function UserProfile() {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<div style={{ background: theme.background, color: theme.text }}>
<h2>Welcome, {user.name}!</h2>
</div>
);
}
4. Implementing Global State
Example: Basic Context Setup
// Create a context
const GlobalStateContext = React.createContext();
// Provider component
function GlobalStateProvider({ children }) {
const [state, setState] = React.useState({
user: null,
theme: 'light',
});
const value = {
state,
dispatch: setState,
};
return (
<GlobalStateContext.Provider value={value}>
{children}
</GlobalStateContext.Provider>
);
}
export { GlobalStateContext, GlobalStateProvider };
| Property | Description | Usage |
|---|---|---|
| React.createContext() | Creates a context object | Initializes global state structure |
| Context.Provider | Provides value to descendants | Wraps components that need access |
| useContext() | Consumes context value | Access global state in components |
Note: Context is ideal for global state like themes, user auth, and language preferences.
Example: Using Context in Components
// Component consuming context
function UserProfile() {
const { state, dispatch } = React.useContext(GlobalStateContext);
return (
<div>
<h2>Welcome, {state.user?.name}</h2>
<p>Theme: {state.theme}</p>
</div>
);
}
// Component updating context
function ToggleTheme() {
const { state, dispatch } = React.useContext(GlobalStateContext);
const toggle = () => {
dispatch(prev => ({
...prev,
theme: prev.theme === 'light' ? 'dark' : 'light',
}));
};
return <button onClick={toggle}>Toggle Theme</button>;
}
Best Practices for Global State with Context
- Use
React.memoto prevent unnecessary re-renders - Keep context values as simple as possible
- Avoid deeply nested contexts; prefer flat structure
- Use
useReducerfor complex state logic
5. Avoiding Context Re-renders
Example: Unnecessary re-renders with Context
// Problem: Every <Child> re-renders when context changes
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext();
function Parent() {
const [theme, setTheme] = React.useState('light');
return (
<ThemeContext.Provider value={theme}>
<Child />
</ThemeContext.Provider>
);
}
function Child() {
const theme = useContext(ThemeContext);
console.log('Child re-rendered');
return <div>Theme: {theme}</div>;
}
| Approach | Description | Impact |
|---|---|---|
| Separate Contexts | Split context into multiple providers for different data types | Reduces unnecessary re-renders by limiting scope |
| Memoized Values | Wrap context value in React.useMemo to prevent object reference changes |
Prevents re-renders when context value is an object with same content |
| Context Reducer | Use useReducer to manage complex context state |
Improves performance by reducing re-renders in large components |
Example: Using React.memo with Context
// Solution: Memoize context value to prevent re-renders
import React, { createContext, useContext, useMemo } from 'react';
const ThemeContext = createContext();
function Parent() {
const [theme, setTheme] = React.useState('light');
const [user, setUser] = React.useState({ name: 'John' });
// Only recompute when theme changes
const contextValue = useMemo(() => ({
theme,
user
}), [theme]);
return (
<ThemeContext.Provider value={contextValue}>
<Child />
</ThemeContext.Provider>
);
}
const Child = React.memo(function Child() {
const { theme } = useContext(ThemeContext);
console.log('Child re-rendered');
return <div>Theme: {theme}</div>;
});
Note:
React.memo only prevents re-renders if the props are shallowly equal. For context, ensure that the object passed to Provider is stable across renders.
Best Practices for Context Re-render Avoidance
- Use
React.useMemoto memoize context values that are objects or arrays - Separate concerns by creating multiple context providers for different data types
- Wrap components with
React.memowhen using context to prevent unnecessary re-renders - Avoid passing functions directly in context as they will cause re-renders on every render
- Use
useReducerfor complex state management to reduce re-renders in large components
6. Using Multiple Contexts
When working with multiple contexts in React Native, you can combine them using the useContext hook or by nesting context providers. This approach allows you to manage different parts of your app's state independently while still accessing them in a single component.
| Context | Usage | Notes |
|---|---|---|
| ThemeContext | useContext(ThemeContext) |
Provides theme information like colors and fonts |
| UserContext | useContext(UserContext) |
Manages user-specific data such as profile and preferences |
Example: Combining Multiple Contexts
import React, { useContext } from 'react';
import { ThemeContext, UserContext } from './contexts';
const UserProfile = () => {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<View style={theme.container}>
<Text style={theme.text}>Welcome, {user.name}!</Text>
<Text style={theme.text}>Your theme is: {theme.name}</Text>
</View>
);
};
export default UserProfile;
Note: When using multiple contexts, consider performance implications. If one context updates frequently, it may cause unnecessary re-renders in components that only depend on other contexts.
7. Creating Context with Default Values
| Property | Description | Default Value | Notes |
|---|---|---|---|
| React.createContext(defaultValue) | Creates a context object | null |
Used to provide default values when no provider is found |
Example: Creating Context with Default Values
import React from 'react';
const ThemeContext = React.createContext({
theme: 'light',
toggleTheme: () => {}
});
export default ThemeContext;
Note: Default values are only used when no matching provider is found in the component tree.
8. Implementing Context Composition
Example: Basic Context Composition
// UserContext.js
import React, { createContext, useContext } from 'react';
const UserContext = createContext();
export const useUser = () => {
const context = useContext(UserContext);
if (!context) {
throw new Error("useUser must be used within a UserProvider");
}
return context;
};
export const UserProvider = ({ children, value }) => (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
// ThemeContext.js
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext();
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
};
export const ThemeProvider = ({ children, value }) => (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
// App.js
import React from 'react';
import { UserProvider } from './UserContext';
import { ThemeProvider } from './ThemeContext';
const App = () => {
const user = { name: 'John', role: 'admin' };
const theme = { primaryColor: '#007bff' };
return (
<UserProvider value={user}>
<ThemeProvider value={theme}>
<MainComponent />
</ThemeProvider>
</UserProvider>
);
};
export default App;
| Context Composition Pattern | Description | Usage |
|---|---|---|
| Nested Providers | Wrapping components with multiple context providers | Allows access to multiple contexts in a component tree |
| Context Isolation | Each context provides its own state and updates | Prevents unnecessary re-renders in components using only one context |
Note: Context composition is useful for organizing complex state management in large applications.
Warning: Avoid deeply nested context providers as they can impact performance and readability.
Best Practices for Context Composition
- Wrap related contexts in a single provider when possible
- Use context composition to avoid prop drilling
- Keep context providers as close to their usage as possible
9. Using Context for Theming
Example: Basic Theme Context Setup
// themes.js
const themes = {
light: {
background: '#ffffff',
text: '#000000',
},
dark: {
background: '#1a1a1a',
text: '#ffffff',
},
};
export const ThemeContext = React.createContext(themes.light);
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme: themes[theme], toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
| Property | Description | Usage |
|---|---|---|
| ThemeContext | React context object for theme values | Used to provide and consume theme data |
| ThemeProvider | Provider component to wrap app with theme context | Wraps root component or parts of app needing theming |
| theme | Current theme object (background, text) | Consumed via useContext(ThemeContext) |
| toggleTheme | Function to switch between themes | Used in UI to change theme mode |
Note: ThemeContext is created with a default light theme to ensure components have a fallback.
Example: Consuming Theme in Components
// ThemedComponent.js
import React, { useContext } from 'react';
import { ThemeContext } from './themes';
const ThemedComponent = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div style={{ background: theme.background, color: theme.text }}>
<h1>Themed Content</h1>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
};
export default ThemedComponent;
Best Practices for Theming with Context
- Define themes as objects with consistent properties (e.g., background, text)
- Use a default theme to prevent undefined values in components
- Provide a toggle function to switch themes dynamically
- Wrap theme providers at the top level of your app or relevant component trees
10. Optimizing Context Performance
Example: Basic Context Usage (Not Optimized)
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext();
export const ThemeProvider = ({ children, theme }) => (
<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>
);
export const useTheme = () => useContext(ThemeContext);
| Approach | Description | Performance Impact | Notes |
|---|---|---|---|
| Single Context | Using one context for all theme values | High | Triggers re-renders for all consumers on any change |
| Split Contexts | Separate contexts for different data types | Low | Reduces unnecessary re-renders |
| Context with Memoization | Using useMemo to memoize context values |
Medium | Prevents unnecessary re-renders when value hasn't changed |
Example: Optimized Context with Memoization
import React, { createContext, useContext, useMemo } from 'react';
const ThemeContext = createContext();
export const ThemeProvider = ({ children, theme }) => {
const value = useMemo(() => ({
...theme,
updateTheme: (newTheme) => {
// Update logic here
}
}), [theme]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => useContext(ThemeContext);
Note: Use
React.memo on components that consume context to prevent unnecessary re-renders.