使用 createContext 的 React 初始值而不是提供一个
React initial value of createContext is used instead of provided one
我正在我的应用程序中使用布尔 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
.[=38= 更改 isDark
布尔值]
为什么 Switch 仍然调用我的初始函数而不是我提供的函数来更改主题?
您的 useTheme()
正在从默认状态获取值,因为在组件树中找不到其上方的 Provider(它处于同一级别)。
只需用 CustomThemeProvider
(或更高级别)包装您的应用程序:
ReactDOM.render(
<CustomThemeProvider>
<App />
</CustomThemeProvider>,
document.getElementById('root')
);
也要小心setDark(!isDark)
,你应该实现它获取以前的状态setDark(state => !state)
因为设置状态被推迟到重新渲染。
顺便问一下,<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>
);
};
Working Stackblitz 记忆分派动作的组件,否则当 theme
更改时它会被 App
组件重新渲染。
这样做只会重新渲染使用该值的组件。
让我link给你一篇我前一天写的文章,内容涉及与 React Context 相关的所有内容,包括优化 React Context, All in One
我正在我的应用程序中使用布尔 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
.[=38= 更改 isDark
布尔值]
为什么 Switch 仍然调用我的初始函数而不是我提供的函数来更改主题?
您的 useTheme()
正在从默认状态获取值,因为在组件树中找不到其上方的 Provider(它处于同一级别)。
只需用 CustomThemeProvider
(或更高级别)包装您的应用程序:
ReactDOM.render(
<CustomThemeProvider>
<App />
</CustomThemeProvider>,
document.getElementById('root')
);
也要小心setDark(!isDark)
,你应该实现它获取以前的状态setDark(state => !state)
因为设置状态被推迟到重新渲染。
顺便问一下,<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>
);
};
Working Stackblitz 记忆分派动作的组件,否则当 theme
更改时它会被 App
组件重新渲染。
这样做只会重新渲染使用该值的组件。
让我link给你一篇我前一天写的文章,内容涉及与 React Context 相关的所有内容,包括优化 React Context, All in One