Skip to main content

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.memo to prevent unnecessary re-renders
  • Keep context values as simple as possible
  • Avoid deeply nested contexts; prefer flat structure
  • Use useReducer for 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.useMemo to memoize context values that are objects or arrays
  • Separate concerns by creating multiple context providers for different data types
  • Wrap components with React.memo when using context to prevent unnecessary re-renders
  • Avoid passing functions directly in context as they will cause re-renders on every render
  • Use useReducer for 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.

Best Practices for Context Performance

  • Split contexts to minimize re-renders
  • Use useMemo for expensive context values
  • Wrap context consumers with React.memo
  • Avoid putting frequently changing data in context

Was this article helpful?