使用反应本机元素更改主题不起作用?
changing theme with react native elements not working?
我一直在使用 React 原生元素。我想在我的应用程序中实现暗模式,但出于某种原因,当我的上下文中的状态发生变化时,我无法让 <ThemeProvider/>
中的主题道具发生变化。
这是我的上下文,其中有我的 darkTheme 和 lightTheme 对象。我还有一个使用 useState 的 lightThemeState
,因此我可以从子组件设置该状态。
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}/>
中的主题没有更新。有人可以解释为什么吗?
您无法使用 React Native Elements 动态更改主题。不幸的是,这在任何地方都没有记录——这一点很重要,因为大多数 RNE 用户会假设他们可以在运行时动态更改整个主题(好吧,我做到了)。
React Native Elements 上有几个已解决的问题 github 提到了这一点。
例如 this issue(2019 年 1 月)其中一位开发者表示:
Currently right now this isn't possible. The ThemeProvider doesn't allow runtime changes to it's props. This is because changes to the props of the ThemeProvider will trigger a rerender for all components under the tree.
更新:
您 可以 动态更改主题,但您必须使用 React Native Elements 提供的 withTheme HOC(例如调用 updateTheme 函数由 withTheme 提供)。有一点额外的布线,但它是可行的。您可以将 HOC 包装在顶层附近,以便主题更新传播到所有子项。
没错。
只是想补充一点,它也可以使用 useContext
。
如果您使用提供程序将您包装到级别组件,然后在您希望能够更改主题的子组件中使用 useContext
挂钩,您可以提取 updateTheme
之类的所以:
const { theme, updateTheme } = useContext(ThemeContext);
它的答案与 starlabs 相同,但另一种方法和我做的方法。
为了与 react-native-elements 一起工作,我尝试动态更新它,但正如楼上指定的那样,唯一的更新方式是这种方式,这是我第一次被迫使用 Consumer这样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}
</>
)
}
您只需在提供商上切换 useDark
即可更新主题颜色。这是一点黑客攻击,但似乎工作得很好。
我有点惊讶,如果以这种方式如此轻松地获得更新,那么目前库中似乎没有其他方法可以在没有 HOC 的情况下获得主题更新,这是一种旧的设计模式。
只需几步即可在 React - Native 中更改主题。
- 首先在你的组件中添加切换按钮
- 创建 Action 和 Reducer 以在 reducer 中保存主题。
- 然后使用 useSelector 获取您保存在 reducer 中的主题名称。
- 然后在index.js文件中根据select主题设置主题。
<NavigationContainer theme={mode == 'light' ? theme.light :[Here is example][1] theme.dark}> {user ? <AppNavigator /> : <AuthNavigator />} </NavigationContainer>
我一直在使用 React 原生元素。我想在我的应用程序中实现暗模式,但出于某种原因,当我的上下文中的状态发生变化时,我无法让 <ThemeProvider/>
中的主题道具发生变化。
这是我的上下文,其中有我的 darkTheme 和 lightTheme 对象。我还有一个使用 useState 的 lightThemeState
,因此我可以从子组件设置该状态。
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}/>
中的主题没有更新。有人可以解释为什么吗?
您无法使用 React Native Elements 动态更改主题。不幸的是,这在任何地方都没有记录——这一点很重要,因为大多数 RNE 用户会假设他们可以在运行时动态更改整个主题(好吧,我做到了)。
React Native Elements 上有几个已解决的问题 github 提到了这一点。 例如 this issue(2019 年 1 月)其中一位开发者表示:
Currently right now this isn't possible. The ThemeProvider doesn't allow runtime changes to it's props. This is because changes to the props of the ThemeProvider will trigger a rerender for all components under the tree.
更新: 您 可以 动态更改主题,但您必须使用 React Native Elements 提供的 withTheme HOC(例如调用 updateTheme 函数由 withTheme 提供)。有一点额外的布线,但它是可行的。您可以将 HOC 包装在顶层附近,以便主题更新传播到所有子项。
没错。
只是想补充一点,它也可以使用 useContext
。
如果您使用提供程序将您包装到级别组件,然后在您希望能够更改主题的子组件中使用 useContext
挂钩,您可以提取 updateTheme
之类的所以:
const { theme, updateTheme } = useContext(ThemeContext);
它的答案与 starlabs 相同,但另一种方法和我做的方法。
为了与 react-native-elements 一起工作,我尝试动态更新它,但正如楼上指定的那样,唯一的更新方式是这种方式,这是我第一次被迫使用 Consumer这样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}
</>
)
}
您只需在提供商上切换 useDark
即可更新主题颜色。这是一点黑客攻击,但似乎工作得很好。
我有点惊讶,如果以这种方式如此轻松地获得更新,那么目前库中似乎没有其他方法可以在没有 HOC 的情况下获得主题更新,这是一种旧的设计模式。
只需几步即可在 React - Native 中更改主题。
- 首先在你的组件中添加切换按钮
- 创建 Action 和 Reducer 以在 reducer 中保存主题。
- 然后使用 useSelector 获取您保存在 reducer 中的主题名称。
- 然后在index.js文件中根据select主题设置主题。
<NavigationContainer theme={mode == 'light' ? theme.light :[Here is example][1] theme.dark}> {user ? <AppNavigator /> : <AuthNavigator />} </NavigationContainer>