使用 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)因为设置状态被推迟到重新渲染。

Working 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>
  );
};

Working Stackblitz 记忆分派动作的组件,否则当 theme 更改时它会被 App 组件重新渲染。

这样做只会重新渲染使用该值的组件。

让我link给你一篇我前一天写的文章,内容涉及与 React Context 相关的所有内容,包括优化 React Context, All in One