admin管理员组文章数量:1289857
I've been working with react native elements. I want to implement a dark mode to my app but for some reason I cant get the theme prop in <ThemeProvider/>
to change when my state in my context changes.
Here is my context where I have my darkTheme and lightTheme object. I also have a lightThemeState
using useState so I can set that state from a child ponent.
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 ponent. 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;
Because I have another context that I use for other logic. I bine <ThemeContextProvider/>
with my other context <JourneyContextProvider/>
. Like so:
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;
Then finally i wrap the whole app in my <CombinedStoreProvider/>
. Like so.
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/binedStore";
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>
);
};
And here is my child ponent where I toggle the lightThemeState
in my context. But even though everything looks great in ThemeContextProvider
(I console log the state and background color and they have succesfully changed the theme). But in this ponent I only get the previous theme. Like nothing changed even though this child ponent rerenders when I toggle the lightThemeState
. I know this because the console log in this ponent logs again after i toggle the theme but the logs show the previous theme colors. Here is the child ponent:
import React, { useContext, useState } from "react";
import { StyleSheet, View, Text } from "react-native";
import { LayoutView, ContainerView } from "../../ponents/styles";
import { ThemeModeContext } from "../../../store/themeStore";
import { Card, ListItem, Avatar, ThemeContext } from "react-native-elements";
import CustomButton from "../../ponents/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;
Maybe there is something wrong with the darkTheme and lightTheme you ask? No, if I changs the state from true to false and reload the app. It works.
Somehow the theme doesnt update in the <ThemeProvider theme={currentTheme}/>
. Can someone explain why?
I've been working with react native elements. I want to implement a dark mode to my app but for some reason I cant get the theme prop in <ThemeProvider/>
to change when my state in my context changes.
Here is my context where I have my darkTheme and lightTheme object. I also have a lightThemeState
using useState so I can set that state from a child ponent.
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 ponent. 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;
Because I have another context that I use for other logic. I bine <ThemeContextProvider/>
with my other context <JourneyContextProvider/>
. Like so:
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;
Then finally i wrap the whole app in my <CombinedStoreProvider/>
. Like so.
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/binedStore";
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>
);
};
And here is my child ponent where I toggle the lightThemeState
in my context. But even though everything looks great in ThemeContextProvider
(I console log the state and background color and they have succesfully changed the theme). But in this ponent I only get the previous theme. Like nothing changed even though this child ponent rerenders when I toggle the lightThemeState
. I know this because the console log in this ponent logs again after i toggle the theme but the logs show the previous theme colors. Here is the child ponent:
import React, { useContext, useState } from "react";
import { StyleSheet, View, Text } from "react-native";
import { LayoutView, ContainerView } from "../../ponents/styles";
import { ThemeModeContext } from "../../../store/themeStore";
import { Card, ListItem, Avatar, ThemeContext } from "react-native-elements";
import CustomButton from "../../ponents/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;
Maybe there is something wrong with the darkTheme and lightTheme you ask? No, if I changs the state from true to false and reload the app. It works.
Somehow the theme doesnt update in the <ThemeProvider theme={currentTheme}/>
. Can someone explain why?
5 Answers
Reset to default 4You can't change the theme dynamically with React Native Elements. Unfortunately this isn't documented anywhere - it's an important point since most users of RNE will assume they can change the entire theme dynamically during runtime (well, I did).
There's a couple of closed issues on React Native Elements github that mention this. For example this issue (Jan 2019) one of the devs stated:
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 ponents under the tree.
UPDATE: You can change the theme dynamically, but you have to use the withTheme HOC that React Native Elements provides (e.g. calling the updateTheme function provided by withTheme). There's a little bit of extra wiring but it's doable. You can wrap the HOC at near the top level so the theme update propagates to all children.
Exacty that.
Just wanted to add that its also possible to use useContext
.
If you wrap you to level ponent with a provider and then use the useContext
hook in the child ponent where you want to be able to change the theme, you can extract the updateTheme
like so:
const { theme, updateTheme } = useContext(ThemeContext);
Its the same answer as starlabs but another way to do it and the way that I did ut.
so as to work with react-native-elements I tried to update it dynamically but as its specified upstair the only way to update it is this way, its been the first time I've been forced to use a Consumer in this way 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 "./ponents/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}
</>
)
}
You can update the theme colors by just toggling useDark
on the Provider. This is a bit of a hack but seems to be working just fine.
I'm a bit surprised that if it's so easy to get an update this way there seems to be no other way in the library currently to get a theme update without the HOC which is an old design pattern.
Change Theme in React - Native in just fews steps.
- First Add toggle button in your ponent
- Create Action and Reducer to Save theme in reducer.
- Then get theme name which you have saved in reducer using useSelector.
- Then in index.js file set theme according to select theme.
<NavigationContainer theme={mode == 'light' ? theme.light :[Here is example][1] theme.dark}> {user ? <AppNavigator /> : <AuthNavigator />} </NavigationContainer>
本文标签: javascriptchanging theme with react native elements not workingStack Overflow
版权声明:本文标题:javascript - changing theme with react native elements not working? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741425487a2378050.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论