Mastering Theme Switching in React with Context API: A Complete Guide
This article explains how to use React's Context API to implement elegant multi‑theme switching, avoid props drilling, manage multiple contexts, and apply memoization techniques to prevent unnecessary re‑renders, improving performance and maintainability.
React Context API Overview
React Context API enables sharing global data across component trees without prop drilling. Ideal for theme settings, authentication state, and application configuration.
Theme Switching Example
Prop‑drilling approach leads to verbose code and unnecessary re‑renders. Example:
function App() {
const theme = 'dark';
return <Parent theme={theme} />;
}
function Parent({ theme }) {
return <Child theme={theme} />;
}
function Child({ theme }) {
return <Switch theme={theme} />;
}
function Switch({ theme }) {
return <button style={{background: theme === 'dark' ? '#000' : '#fff'}}>
Toggle Theme
</button>;
}Context solution:
// src/contexts/ThemeContext.js
import { createContext } from 'react';
export const themes = {
light: { background: '#fff', text: '#000', current: 'light' },
dark: { background: '#000', text: '#fff', current: 'dark' }
};
export const ThemeContext = createContext(themes.light); // src/App.js
import React, { useState } from 'react';
import { ThemeContext, themes } from './contexts/ThemeContext';
import Navbar from './components/Navbar';
import Switch from './components/Switch';
export default function App() {
const [theme, setTheme] = useState(themes.light);
const toggleTheme = () => setTheme(t => t === themes.light ? themes.dark : themes.light);
return (
<div className="App">
<ThemeContext.Provider value={theme}>
<Navbar />
<Switch changeTheme={toggleTheme} />
</ThemeContext.Provider>
</div>
);
} // src/components/Switch.js
import React, { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
export default function Switch({ changeTheme }) {
const theme = useContext(ThemeContext);
return (
<button
style={{ backgroundColor: theme.background, color: theme.text }}
onClick={changeTheme}
>
Toggle Theme: {theme.current}
</button>
);
} // src/components/Navbar.js
import React, { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
export default function Navbar() {
const theme = useContext(ThemeContext);
return (
<div style={{ backgroundColor: theme.background }}>
<ul style={{ display: 'flex', gap: '20px' }}>
<li style={{ color: theme.text }}>Home</li>
<li style={{ color: theme.text }}>Square</li>
<li style={{ color: theme.text }}>Profile</li>
</ul>
</div>
);
}Resulting UI shows light and dark modes.
Creating Multiple Contexts
Separate concerns by defining distinct contexts, e.g., ThemeContext and UserContext, to avoid unrelated re‑renders.
// src/contexts/UserContext.js
import { createContext } from 'react';
export const UserContext = createContext({ username: '', age: 0 }); // src/App.js (combined providers)
import React, { useState } from 'react';
import { ThemeContext, themes } from './contexts/ThemeContext';
import { UserContext } from './contexts/UserContext';
import Navbar from './components/Navbar';
import Switch from './components/Switch';
export default function App() {
const [theme, setTheme] = useState(themes.light);
const [user, setUser] = useState({ username: 'mario', age: 25 });
const toggleTheme = () => setTheme(t => t === themes.light ? themes.dark : themes.light);
return (
<div className="App">
<ThemeContext.Provider value={theme}>
<UserContext.Provider value={user}>
<Navbar />
<Switch changeTheme={toggleTheme} />
</UserContext.Provider>
</ThemeContext.Provider>
</div>
);
}Preventing Unnecessary Re‑renders
Even with React.memo, components that consume a context re‑render when the context value changes. Apply the following techniques:
1. Use Multiple Contexts
Separate state into different contexts so only components that depend on a specific context update.
2. Split Components and Memoize
Wrap sub‑components with React.memo and pass only needed values as props.
const Card = () => {
const { theme } = useContext(AppContext);
return (
<div>
<CardTitle theme={theme} />
<CardDescription theme={theme} />
</div>
);
};
const CardTitle = React.memo(({ theme }) => (
<h2 style={{ color: theme.text }}>2024 Paris Olympics</h2>
));
const CardDescription = React.memo(({ theme }) => (
<p style={{ color: theme.text }}>2024 Paris Olympics is the 33rd Summer Games</p>
));3. Memoize Expensive Rendering
Use useMemo to cache the rendered subtree when the dependent values (e.g., theme) do not change.
const Card = () => {
const { theme } = useContext(AppContext);
return useMemo(() => (
<div>
<CardTitle theme={theme} />
<CardDescription theme={theme} />
</div>
), [theme]);
};These patterns reduce the render cost of context‑driven components, improving performance in large React applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
