2

我一直在使用 react native 元素。我想为我的应用程序实现暗模式,但由于某种原因,<ThemeProvider/>当我在上下文中的状态发生变化时,我无法让主题道具改变。

这是我的上下文,其中有我的 darkTheme 和 lightTheme 对象。我也有一个lightThemeStateusing useState,因此我可以从子组件设置该状态。

import React, { createContext, useState, useEffect } from "react";
import { AsyncStorage } from "react-native";

import { ThemeProvider } from "react-native-elements";
import lightTheme from "../themes/light";
import darkTheme from "../themes/dark";

export const ThemeModeContext = createContext();

export const ThemeContextProvider = (props) => {
  const [lightThemeState, setLightThemeState] = useState(true);

  const saveThemeState = async () => {
    if (lightThemeState) {
      await AsyncStorage.removeItem("lightThemeState");
    } else {
      await AsyncStorage.setItem(
        "lightThemeState",
        JSON.stringify(lightThemeState)
      );
    }
  };

  const getThemeState = async () => {
    currentMode = await AsyncStorage.getItem("lightThemeState");

    if (currentMode) {
      setLightThemeState(JSON.parse(currentMode));
    }
  };

  useEffect(() => {
    saveThemeState();
  }, [lightThemeState]);

  useEffect(() => {
    getThemeState();
  }, []);

  const currentTheme = lightThemeState ? lightTheme : darkTheme;

  console.log("LIGHT THEME STATE", lightThemeState); 
// When I log this after I used the setLigthThemeState in a child component. It gives the correct state ie true or false.
  console.log("COLOR OF THE THEMES BACKGROUND", currentTheme.colors.background);
// This also gives the correct background for the theme that is the "currentTheme" depending on the state. So this far, everything is correct.

  return (
    <ThemeModeContext.Provider value={[lightThemeState, setLightThemeState]}>
      <ThemeProvider theme={currentTheme}>{props.children}</ThemeProvider>
    </ThemeModeContext.Provider>
  );
};

export default ThemeContextProvider;

因为我有另一个用于其他逻辑的上下文。我结合<ThemeContextProvider/>我的其他上下文<JourneyContextProvider/>。像这样:

import React from "react";
import ThemeContextProvider from "./themeStore";
import JourneyContextProvider from "./journeyStore";

export const CombinedStoreProvider = (props) => {
  return (
    <JourneyContextProvider>
      <ThemeContextProvider>{props.children}</ThemeContextProvider>
    </JourneyContextProvider>
  );
};

export default CombinedStoreProvider;

然后最后我将整个应用程序包装在我的<CombinedStoreProvider/>. 像这样。

import React from "react";
import { SafeAreaView } from "react-native";

import { createAppContainer, createSwitchNavigator } from "react-navigation";
import { createMaterialBottomTabNavigator } from "react-navigation-material-bottom-tabs";

import Icon from "react-native-vector-icons/Ionicons";

import MoreScreenfrom "./src/screens/MoreModal";
import CombinedStoreProvider from "./store/combinedStore";

const TabNavigator = createMaterialBottomTabNavigator(
  {
    MoreScreen: {
      screen: MoreScreen,
      navigationOptions: {
        title: "More",
        tabBarIcon: ({ tintColor }) => (
          <SafeAreaView>
            <Icon style={[{ color: tintColor }]} size={25} name={"ios-more"} />
          </SafeAreaView>
        ),
      },
    },
  },
  {
    theme: ({ darkTheme }) => console.log(darkTheme),
    barStyleDark: {
      backgroundColor: darkTheme.colors.background,
    },
    barStyleLight: {
      backgroundColor: lightTheme.colors.background,
    },
    shifting: false,
    labeled: true,
    initialRouteName: "MoreScreen",
    activeColor: "#E4DC93",
    inactiveColor: "#fff",
    barStyle: { backgroundColor: "transparent", height: 80, paddingTop: 10 },
  }
);

const AllRoutes = createSwitchNavigator(
  {
    PersonalSettings: {
      title: "Personal Settings",
      screen: PersonalSettings,
      header: ({ goBack }) => ({
        left: (
          <Icon
            name={"chevron-left"}
            onPress={() => {
              goBack();
            }}
          />
        ),
      }),
    },
    Tabs: {
      screen: TabNavigator,
    },
  },
  {
    initialRouteName: "Tabs",
  }
);

const AppContainer = createAppContainer(AllRoutes);

export default App = () => {
  return (
    <CombinedStoreProvider>
      <AppContainer />
    </CombinedStoreProvider>
  );
};

这是我lightThemeState在上下文中切换的子组件。但即使一切看起来都很棒ThemeContextProvider(我控制台记录了状态和背景颜色,他们已经成功地改变了主题)。但是在这个组件中,我只得到了以前的主题。即使这个子组件在我切换lightThemeState. 我知道这一点,因为在我切换主题后控制台登录该组件再次记录,但日志显示以前的主题颜色。这是子组件:

import React, { useContext, useState } from "react";
import { StyleSheet, View, Text } from "react-native";
import { LayoutView, ContainerView } from "../../components/styles";
import { ThemeModeContext } from "../../../store/themeStore";
import { Card, ListItem, Avatar, ThemeContext } from "react-native-elements";

import CustomButton from "../../components/CustomButton";

const INITIAL_PERSONAL_INFO_STATE = {
  name: "",
  username: "",
  profileImage: "",
  favoriteDestinations: [],
};

const MoreModal = (props) => {
  const [personalInfo, setPersonalInfo] = useState(INITIAL_PERSONAL_INFO_STATE);

  const [lightThemeState, setLightThemeState] = useContext(ThemeModeContext);
  const { theme } = useContext(ThemeContext);
  const { navigate } = props.navigation;

  const primaryColor = theme.colors.background;

  console.log("COLOR IN COMPONENT", primaryColor);
// The color is from the previous theme and even thou the state has changed in the state below
  console.log("LIGHT THEME STATE IN COMPONENT", lightThemeState);

  return (
    <LayoutView primaryColor={theme.colors.background}>
      <ContainerView>
        <View>
        </View>
        <Card
          title={"Settings"}
        >
          <ListItem
            title="Light mode"
            switch={{
              value: lightThemeState,
              onValueChange: (value) => setLightThemeState(value), 
// Here is where I set lighThemeState to false in my context

            }}
            bottomDivider
        </Card>
      </ContainerView>
      <CustomButton title={"Sign in"}></CustomButton>
    </LayoutView>
  );
};

export default MoreModal;

也许你问的darkTheme和lightTheme有问题?不,如果我将状态从 true 更改为 false 并重新加载应用程序。有用。不知何故,主题在<ThemeProvider theme={currentTheme}/>. 有人可以解释为什么吗?

4

5 回答 5

4

您不能使用 React Native Elements 动态更改主题。不幸的是,这在任何地方都没有记录——这是很重要的一点,因为大多数 RNE 用户会假设他们可以在运行时动态更改整个主题(嗯,我做到了)。

React Native Elements github 上有几个已关闭的问题提到了这一点。例如本期(2019 年 1 月),其中一位开发人员表示:

目前这是不可能的。ThemeProvider 不允许对其道具进行运行时更改。这是因为对 ThemeProvider 的 props 的更改将触发树下所有组件的重新渲染。

更新:你可以动态改变主题,但你必须使用 React Native Elements 提供的withTheme HOC(例如调用 withTheme 提供的 updateTheme 函数)。有一点额外的布线,但它是可行的。您可以将 HOC 包装在靠近顶层的位置,以便主题更新传播到所有子级。

于 2020-04-22T04:07:08.113 回答
2

正是如此。只是想补充一点,它也可以使用useContext.

如果您使用提供者将您包装到 level 组件,然后useContext在您希望能够更改主题的子组件中使用钩子,您可以updateTheme像这样提取:

const { theme, updateTheme } = useContext(ThemeContext);

它与starlabs 的答案相同,但另一种方法和我做的方式。

于 2020-04-22T19:02:04.890 回答
0

您只需切换useDarkProvider 即可更新主题颜色。这有点小技巧,但似乎工作得很好。

我有点惊讶,如果以这种方式获得更新如此容易,那么库中目前似乎没有其他方法可以在没有 HOC(这是一种旧的设计模式)的情况下获得主题更新。

于 2021-02-16T09:19:29.517 回答
0

为了与 react-native-elements 一起工作,我尝试动态更新它,但正如楼上指定的那样,唯一更新它的方法是这种方式,这是我第一次被迫以这种方式使用消费者 XD:

//App.js
import 'react-native-gesture-handler';
import React from "react";
import { Provider } from "react-redux";
import { store } from "./redux";
import { TranslationProvider, ColorProvider } from "./context";
import { ThemeProvider } from "react-native-elements";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { appTheme as theme } from './theme';
import App from "./navigation";
import ThemeConsumer from "./components/consumers/themeConsumer";

    export default () =>
        <Provider store={store}>
            <SafeAreaProvider>
                <TranslationProvider>
                    <ColorProvider>
                        <ThemeProvider theme={theme}>
                            <ThemeConsumer>
                                <App />
                            </ThemeConsumer>
                        </ThemeProvider>
                    </ColorProvider>
                </TranslationProvider>
            </SafeAreaProvider>
        </Provider>
    
    

//themeConsumer.js

import React, { useContext, useEffect } from 'react';
import { ThemeContext } from "react-native-elements";
import { color } from '../../colors';
import { useTheme } from '../../context';

export default ({ children }) => {
    const { updateTheme } = useContext(ThemeContext);
    const { isDarkMode } = useTheme();
    useEffect(() => {
        updateTheme({ colors: color });
    }, [isDarkMode()])
    return (
        <>
            {children}
        </>
    )
}
于 2021-02-02T14:12:30.920 回答
0

在 React 中更改主题 - 只需几个步骤即可实现原生。

  1. 首先在组件中添加切换按钮
  2. 创建 Action 和 Reducer 以在 reducer 中保存主题。
  3. 然后使用 useSelector 获取您保存在 reducer 中的主题名称。
  4. 然后在 index.js 文件中根据选择主题设置主题。 <NavigationContainer theme={mode == 'light' ? theme.light :[Here is example][1] theme.dark}> {user ? <AppNavigator /> : <AuthNavigator />} </NavigationContainer>
于 2021-11-01T07:10:27.747 回答