我在我的应用程序中创建了一个布尔值isDark
的上下文。布尔值isDark
是用useState
创建的,我提供了这个布尔值和一个函数来将布尔值更改为ThemeContext
,以便在组件树中进一步访问它。
在下面,我创建了ThemeContext
,布尔值初始化为false
,并在控制台中创建了一个函数,该函数仅警告正在使用初始值:
//ThemeContext.tsx
export type ContextType = {
isDark: boolean
toggleTheme: () => void
}
const ThemeContext = createContext<ContextType>({
isDark: false,
toggleTheme: () => console.warn('Still using initial value'),
})
export const useTheme = () => useContext(ThemeContext)
export default ThemeContext
这里我提供了主题和功能,通过toggleTheme
函数来改变它:
//CustomThemeProvider.tsx
export const CustomThemeProvider: React.FC = ({ children }) => {
const [isDark, setDark] = useState(false)
const toggleTheme = () => {
console.log('Change theme')
setDark(!isDark)
}
const providerTheme = useMemo(
() => ({ isDark, toggleTheme }),
[isDark, toggleTheme],
)
return (
<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
<ThemeContext.Provider value={providerTheme}>
{children}
</ThemeContext.Provider>
</ThemeProvider>
)
}
我现在想访问布尔值和toggleTheme
函数,并通过我在开始时创建的自定义钩子(useTheme
)来实现,它只使用useContext
:
//App.tsx
export default function App() {
const { isDark, toggleTheme } = useTheme()
return (
<CustomThemeProvider>
<Box flex={1} justifyContent="center">
<Paper title="Test Title">
<Switch onValueChange={toggleTheme} value={isDark} />
</Box>
</CustomThemeProvider>
)
}
当我现在尝试用Switch
组件(React Native)切换主题时,我得到控制台警告,我的初始函数正在被调用。这意味着我的toggleTheme
函数仍然是初始函数() => console.warn('Still using initial value')
,即使我提供了一个新函数,应该用我的ThemeContext.Provider
改变isDark
布尔值。
为什么我的初始函数仍然被交换机调用,而不是我提供的一个来改变主题?
您的useTheme()
正在从默认状态获取值,因为在组件树中没有找到它上面的Provider(它在同一级别)。
只需用CustomThemeProvider
(或更高级别)包装您的应用程序:
ReactDOM.render(
<CustomThemeProvider>
<App />
</CustomThemeProvider>,
document.getElementById('root')
);
也要小心setDark(!isDark)
,你应该实现它获得以前的状态setDark(state => !state)
,因为设置状态被推迟到重新渲染。
工作Stackblitz
顺便问一下,<ThemeProvider theme={isDark ? darkTheme : lightTheme}>
,这行是不是打错了?如果您试图将上下文分成两部分(值和分派,这是一个好主意),我会这样做:
const ThemeContext = createContext({
isDark: false
});
export const useTheme = () => useContext(ThemeContext);
export default ThemeContext;
const ToggleThemeContext = createContext({
toggleTheme: () => console.warn('Still using initial value')
});
export const useToggleTheme = () => useContext(ToggleThemeContext);
export default ToggleThemeContext;
//CustomThemeProvider.tsx
export const CustomThemeProvider = ({ children }) => {
const [isDark, setDark] = useState(false);
const memoToggleTheme = useCallback(() => setDark(state => !state), [
setDark
]);
return (
<ToggleThemeContext.Provider value={memoToggleTheme}>
<ThemeContext.Provider value={isDark}>{children}</ThemeContext.Provider>
</ToggleThemeContext.Provider>
);
};
工作Stackblitz记忆的组件调度的动作,否则它将被重新呈现由theme
组件当CC_22的变化。
通过这样做,只有使用该值的组件才会被重新渲染。
让我给你链接一篇我昨天写的关于React Context的文章,包括优化React Context, All in One