使用主题 Context 实现深色模式切换
构建一个存储当前配色方案的 ThemeContext,公开切换函数,并将主题颜色应用到应用中的各个组件。
使用主题 Context 实现深色模式切换 是 CoddyKit 上的免费 React Native Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Native Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Native Academy 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
Why a Theme Context?
A theme context centralizes color and typography values so that every component in the app reads from a single source of truth. When the user switches between light and dark mode, updating the context value triggers a re-render of all consuming components with the new colors — no prop drilling or manual updates required.
Defining Light and Dark Theme Objects
Start by defining two theme objects with the same keys. Using identical keys for both themes means every component can apply a color without knowing which mode is active — it just reads from the current theme object and both themes handle the rest.
export const lightTheme = {
background: '#ffffff',
text: '#1a1a2e',
card: '#f5f5f5',
primary: '#6200ee',
border: '#e0e0e0',
};
export const darkTheme = {
background: '#1a1a2e',
text: '#e8e8f0',
card: '#16213e',
primary: '#bb86fc',
border: '#333355',
};Creating the ThemeContext
Create a ThemeContext that holds the current theme object and a toggle function. The default value mirrors the shape of what the Provider will supply, making it safe to destructure in consumers even if the Provider is accidentally missing in tests.
import { createContext, useContext, useState } from 'react';
import { lightTheme, darkTheme } from './themes';
export const ThemeContext = createContext({
theme: lightTheme,
isDark: false,
toggleTheme: () => {},
});Building the ThemeProvider
The ThemeProvider manages a boolean isDark state and derives the active theme object from it. The toggleTheme function flips the boolean. Memoize the context value with useMemo so that a new object reference is only created when isDark actually changes.
export function ThemeProvider({ children }) {
const [isDark, setIsDark] = useState(false);
const value = useMemo(() => ({
theme: isDark ? darkTheme : lightTheme,
isDark,
toggleTheme: () => setIsDark((d) => !d),
}), [isDark]);
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}A useTheme Custom Hook
Export a useTheme hook that wraps useContext(ThemeContext). Components call useTheme() for a clean, descriptive import instead of remembering to pass the context object. The hook also throws a helpful error if called outside a ThemeProvider.
export function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error('useTheme must be inside ThemeProvider');
return ctx;
}Applying Theme Colors to a Component
Any component that reads from useTheme will automatically display the correct colors for the current mode. Pass the theme colors into inline styles or StyleSheet styles. When the theme toggles, all consuming components re-render with the new values.
function Card({ title, body }) {
const { theme } = useTheme();
return (
<View style={{
backgroundColor: theme.card,
borderColor: theme.border,
borderWidth: 1,
borderRadius: 8,
padding: 16,
}}>
<Text style={{ color: theme.text, fontWeight: 'bold' }}>{title}</Text>
<Text style={{ color: theme.text }}>{body}</Text>
</View>
);
}A Toggle Switch in the Settings Screen
Add a dark mode switch to the settings screen. The Switch component's value is isDark from the theme context, and onValueChange calls toggleTheme. No local state needed — the toggle directly controls the global theme state.
function SettingsScreen() {
const { isDark, toggleTheme, theme } = useTheme();
return (
<View style={{ backgroundColor: theme.background, flex: 1, padding: 24 }}>
<Text style={{ color: theme.text }}>Dark Mode</Text>
<Switch
value={isDark}
onValueChange={toggleTheme}
trackColor={{ true: theme.primary }}
/>
</View>
);
}Persisting the Theme Preference
Save the user's theme preference to AsyncStorage so it survives app restarts. In ThemeProvider, load the saved preference in a useEffect on mount and initialize state accordingly. Save the new value in toggleTheme whenever the user switches modes.
export function ThemeProvider({ children }) {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
AsyncStorage.getItem('theme').then((saved) => {
if (saved === 'dark') setIsDark(true);
});
}, []);
const toggleTheme = () => {
const next = !isDark;
setIsDark(next);
AsyncStorage.setItem('theme', next ? 'dark' : 'light');
};
// ...
}Respecting the System Theme
React Native's useColorScheme hook returns the device's current color scheme ('light' or 'dark'). You can use this as the default theme instead of always starting in light mode. If the user has not explicitly set a preference, respecting the system setting gives the best out-of-the-box experience.
import { useColorScheme } from 'react-native';
function ThemeProvider({ children }) {
const systemScheme = useColorScheme();
const [isDark, setIsDark] = useState(systemScheme === 'dark');
// ...
}Applying Theme to Navigation Header
The navigation header can also respond to theme changes. Pass a function to screenOptions on the navigator that reads from the theme context and returns header style options. When the theme changes, the navigator re-evaluates the options and updates the header colors accordingly.
function AppStack() {
const { theme } = useTheme();
return (
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: theme.card },
headerTintColor: theme.text,
}}
>
<Stack.Screen name='Home' component={HomeScreen} />
</Stack.Navigator>
);
}Preventing Flicker on Theme Load
When loading the saved theme from AsyncStorage on startup, there is a brief moment before the preference is restored. Prevent a flash of the wrong theme by showing a splash screen or delaying navigation until the theme is loaded. Use an isThemeReady state in the provider and render null until it is true.
const [isDark, setIsDark] = useState(false);
const [ready, setReady] = useState(false);
useEffect(() => {
AsyncStorage.getItem('theme').then((saved) => {
if (saved === 'dark') setIsDark(true);
setReady(true);
});
}, []);
if (!ready) return null; // prevent flashQuick Check
Test your understanding of theme context and dark mode from this lesson.
Lesson Recap
In this lesson you learned: define separate light and dark theme objects with matching keys, provide both the active theme and a toggleTheme function through context, and persist the theme preference to AsyncStorage and restore it on app startup. Next up we explore avoiding context performance pitfalls.
常见问题解答
「使用主题 Context 实现深色模式切换」课时是免费的吗?
是的 — 「使用主题 Context 实现深色模式切换」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Native Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Native Academy 课程共包含 4 节课。
「使用主题 Context 实现深色模式切换」这节课中我会学到什么?
构建一个存储当前配色方案的 ThemeContext,公开切换函数,并将主题颜色应用到应用中的各个组件。 你通过在浏览器中直接运行的动手代码来练习 React Native Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Native Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Native Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用主题 Context 实现深色模式切换」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Native Academy 课中编写并运行代码吗?
能。每节 React Native Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 创建并提供 Context
- 使用 useContext 消费 Context
- 使用主题 Context 实现深色模式切换
- 避免 Context 性能问题