typescript - 在使用 expo 的情况下,AsyncStorage 在应用重载之间无法保存(TS/React Native)
我有一个上下文,它在我的应用中存储一个 theme 变量的状态。最初它被设置为 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}
它包装了我的整个应用 App.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>
);
}
然后在一个钩子中使用它
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
}
然后我有一个组件用于设置theme的值:
设置切换组件.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>
)
}
当我使用组件切换主题时,主题会在 AsyncStorage 中正确更新,但当我从终端重新加载应用或重启 Expo 应用时,AsyncStorage 不再保留之前设置的主题。
答案#1
这是由我在 AppSettingsContextProvider 中写的一个拼写错误引起的,我一直在将 asyncWrap 的返回值与主题的字符串化版本进行比较。
代码应该是:
asyncWrap().then((value) => {
if (value.theme === Themes.EMPTY) {
setTheme(Themes.LIGHT);
} else {
setTheme(value.theme);
}
})
这意味着一旦获取了存储的值,空的默认值将被覆盖,因为theme的默认值已经被设置。
