изменение темы с реагировать родные элементы не работает? - PullRequest
0 голосов
/ 18 апреля 2020

Я работал с реагирующими нативными элементами. Я хочу реализовать темный режим в своем приложении, но по какой-то причине я не могу заставить опору темы в <ThemeProvider/> меняться при изменении моего состояния в моем контексте.

Вот мой контекст, в котором у меня есть моя darkTheme и Объект lightTheme. У меня также есть lightThemeState с использованием 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;

Поскольку у меня есть другой контекст, который я использую для других логи c. Я комбинирую <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;

Может быть, что-то не так с темной темой и светлой темой, спросите вы? Нет, если я изменю состояние с истинного на ложное и перезагрузлю приложение. Оно работает. Каким-то образом тема не обновляется в <ThemeProvider theme={currentTheme}/>. Может кто-нибудь объяснить, почему?

Ответы [ 2 ]

1 голос
/ 22 апреля 2020

Вы не можете изменить тему динамически с React Native Elements . К сожалению, это нигде не задокументировано - это важный момент, так как большинство пользователей RNE предполагают, что они могут динамически изменять всю тему во время выполнения (ну, я так и сделал).

На React Native есть пара закрытых проблем Элементы github, которые упоминают об этом. Например, эта проблема (январь 2019), один из разработчиков сказал:

В настоящее время это невозможно. ThemeProvider не допускает изменения времени выполнения для его реквизита. Это происходит потому, что изменения в реквизитах ThemeProvider вызовут повторное рендеринг для всех компонентов в дереве.

ОБНОВЛЕНИЕ : Вы можете меняет тему динамически, но вы должны использовать withTheme HO C, который предоставляет React Native Elements (например, вызывая функцию updateTheme, предоставляемую withTheme). Есть немного дополнительной проводки, но это выполнимо. Вы можете обернуть HO C на верхнем уровне, чтобы обновление темы распространялось на всех детей.

0 голосов
/ 22 апреля 2020

Именно это. Просто хотел добавить, что его также можно использовать useContext.

Если вы добавили компонент к уровню с провайдером, а затем использовали хук useContext в дочернем компоненте, где вы хотите изменить тему, вы можете извлечь updateTheme следующим образом:

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

Это тот же ответ, что и у звездных, но другой способ сделать это и то, что я сделал, ут.

...