I have a context which stores the state for a theme
variable in my app.Initially it is set to empty
.
AppSettingsContext.tsx
import React, {createContext, useState, useEffect} from "react";import { AppSettingsContextStruct, Themes } from "../../types";import AsyncStorage from "@react-native-async-storage/async-storage";const fetchThemeFromStorage = async () => { return AsyncStorage.getItem("THEME") .then((value) => { if (value === `"LIGHT"`) { return Themes.LIGHT; }; if (value === `"DARK"`) { return Themes.DARK; }; if (value === `"empty"` || "empty") { return Themes.EMPTY; }; }) .catch((err) => { console.log(err); return Themes.LIGHT; });};const defaultVal: AppSettingsContextStruct = { theme: Themes.EMPTY, setTheme: () => {},}type CtxProps = { children: JSX.Element}const AppSettingsContext = createContext(defaultVal);const AppSettingsContextProvider:React.FC<CtxProps> = (props) => { const [theme, setTheme] = useState<Themes>(Themes.EMPTY); useEffect(() => { const asyncWrap = async () => { const storageTheme = await fetchThemeFromStorage(); return { theme: storageTheme, } } asyncWrap().then((value) => { console.log("asyncWrap ran:", value) if (value.theme === JSON.stringify(Themes.EMPTY)) { setTheme(Themes.LIGHT); }; }) }, []); useEffect(() => { AsyncStorage.setItem("THEME", JSON.stringify(theme)).then(() => { console.log(" > ASYNC STORAGE UPDATED THEME ->", theme) }); },[theme]); return (<AppSettingsContext.Provider value={{theme,setTheme}}> {props.children}</AppSettingsContext.Provider> )}export {AppSettingsContext, AppSettingsContextProvider}
Which wraps my entire appApp.tsx
import { NavigationContainer } from '@react-navigation/native';import RootNavigator from './navigator/RootNavigator';import {UserContextProvider} from "./components/contexts/UserContext"import {AppSettingsContextProvider} from "./components/contexts/AppSettingsContext"export default function App() { return ( <UserContextProvider><AppSettingsContextProvider> <NavigationContainer><RootNavigator /></NavigationContainer></AppSettingsContextProvider></UserContextProvider> );}
This is then used within a hook
useAppSettingsContext.ts
import { useContext } from "react"import { AppSettingsContext } from "../components/contexts/AppSettingsContext"export const useAppSettingsContext = () => { const context = useContext(AppSettingsContext); if (context === undefined) { throw new Error("useUserContext was used outside of its Provider"); } return context}
Then I have a component to set the value of theme:
SettingsToggleComponent.tsx
import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons';import { useEffect, useState } from 'react';import { TouchableOpacity, Text, StyleSheet, View } from "react-native"import { useAppSettingsContext } from '../../hooks/useAppSettingsContext';import { Themes } from '../../types';import AsyncStorage from '@react-native-async-storage/async-storage';type Props = { id: string; settingName: string; iconName: string; sendActionLower: Function;};const SettingsToggleComponent: React.FC<Props> = ( {id, settingName, sendActionLower} ) => { const { theme, setTheme } = useAppSettingsContext(); const [isLight, setIsLight] = useState<boolean>(true); useEffect(() => { if (theme === Themes.LIGHT) { setIsLight(true); } else if (theme === Themes.DARK) { setIsLight(false); } else { setIsLight(false); } }, []) return (<TouchableOpacity onPress={() => { //changing asyncStorage will cause rerender at root? -> false if (theme === Themes.LIGHT) { setIsLight(false) setTheme(Themes.DARK) } else { setIsLight(true) setTheme(Themes.LIGHT) } }}><View style={styles.settingLayout}> {/* Make sure that the possible names for the icons are used instead of random icon names */}<View style={styles.iconContainer}><MaterialCommunityIcons name="theme-light-dark" size={24} color="#5a5a5a" /></View><View style={styles.textContainer}><View style={styles.textContainerLayout}><Text style={styles.settingName}>{settingName}</Text><View style={styles.arrowSettingValueContainer}> { isLight?<Ionicons name="ios-sunny-sharp" size={24} color="#5A5A5A" /> : <Ionicons name="ios-moon-sharp" size={24} color="#5A5A5A" /> }</View></View></View></View></TouchableOpacity> )}
When I switch the theme using the component the theme updates in AsyncStorage correctly but when i reload the app from within the terminal or by restarting the expo app, AsyncStorage no longer holds the theme that was previously set.