【问题标题】:React initial value of createContext is used instead of provided one使用 createContext 的 React 初始值,而不是提供一个
【发布时间】:2021-08-11 21:23:18
【问题描述】:

我正在我的应用程序中创建一个带有布尔值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 函数仍然是初始函数() =&gt; console.warn('Still using initial value'),即使我提供了一个新函数,它应该用我的ThemeContext.Provider 更改isDark 布尔值。
为什么我的初始函数仍然被 Switch 调用,而不是我提供的函数来更改主题?

【问题讨论】:

    标签: react-native react-hooks use-context


    【解决方案1】:

    您的useTheme() 正在从默认状态获取值,因为在组件树中未找到其上方的 Provider(它处于同一级别)。

    只需使用您的CustomThemeProvider(或更高级别)包装您的应用程序:

    ReactDOM.render(
      <CustomThemeProvider>
        <App />
      </CustomThemeProvider>,
      document.getElementById('root')
    );
    

    也要小心setDark(!isDark),你应该实现它来获取先前的状态setDark(state =&gt; !state),因为设置状态被推迟到重新渲染。

    Working Stackblitz

    顺便说一句,&lt;ThemeProvider theme={isDark ? darkTheme : lightTheme}&gt;,那行是错字吗?如果您尝试将上下文一分为二(值和调度,这是一个好主意),我会这样做:

    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 组件将重新渲染它。

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

    让我链接你一篇我在最后一天写的关于 React Context 的所有内容的文章,包括优化React Context, All in One

    【讨论】:

    • 效果很好。我将阅读这篇文章。我与ThemeProvder 的行不是错字,因为那是'@Sopify/restyle' 的提供者,我用它来为我的应用程序设置主题。所以我只是根据 isDark 布尔值更改提供的主题。
    猜你喜欢
    • 2021-06-17
    • 2020-11-14
    • 1970-01-01
    • 1970-01-01
    • 2017-10-02
    • 1970-01-01
    • 2020-12-24
    • 2022-11-15
    • 2020-11-14
    相关资源
    最近更新 更多