Zoeken op website

typescript - AsyncStorage slaat niets op tussen het herladen van de app met expo (TS/React Native)


Ik heb een context die de status opslaat voor een theme-variabele in mijn app. Aanvankelijk is deze ingesteld op 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}

Die mijn hele app App.tsx omsluit

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>
      );
}

Dit wordt dan gebruikt binnen een 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
}

Dan heb ik een component om de waarde van theme in te stellen:

InstellingenSchakelComponent.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>
    )
}

Wanneer ik het thema wijzig met behulp van de component, wordt het thema correct bijgewerkt in AsyncStorage, maar wanneer ik de app opnieuw laad vanuit de terminal of door de Expo-app opnieuw te starten, houdt AsyncStorage het eerder ingestelde thema niet meer vast.

Antwoord #1

Dit werd veroorzaakt door een typefout die ik had geschreven binnen AppSettingsContextProvider, ik had de returnwaarde van asyncWrap vergeleken met de geïnterpreteerde versie van het thema.

De code had moeten zijn:

        asyncWrap().then((value) => {
            if (value.theme === Themes.EMPTY) {
                setTheme(Themes.LIGHT);
            } else {
                setTheme(value.theme);
            }
        })

Dit betekent dat de standaardwaarde van leeg zal worden overschreven zodra de opgeslagen waarde is opgehaald, aangezien de standaardwaarde van theme al zal zijn ingesteld.