reactjs - react-native'de AsyncStorage kullanarak state değeri güncellenmiyor
Async storage'a bir değer kaydetmeye çalışıyorum ve ardından Async storage'dan gelen değere bağlı olarak doğru sayfaya yönlendirme yapmak istiyorum. AsyncStorage'a veri kaydedebiliyorum ama durumlarım güncellenmiyor, durumun güncellenmesi için uygulamayı yeniden yüklemem gerekiyor. İşte kodum:
İşte burada bir Karşılama/Onboarding ekranım var. Bu ekranın sadece yeni uygulama kullanıcılarına gösterilmesini istiyorum. Yani bir kullanıcı devam butonuna bastığında, bir değerini Async storage'a kaydetmek istiyorum, böylece bir sonraki girişlerinde onboarding sayfasını tekrar görmeleri gerekmez. İşte benim Onboarding sayfam:
const WelcomeScreen: FC<IWelcomeScreen> = ({ navigation }) => {
const { width, height } = Dimensions.get("window");
const btnText = "Contiunue";
const title = "Book";
const subTitle = "Fab";
let [fontsLoaded] = useFonts({
PinyonScript_400Regular,
});
const continueBtn = async () => {
try {
await AsyncStorage.setItem('@viewedOnboarding', 'true');
} catch (error) {
console.log('Error @setItem: ', error);
};
};
if (!fontsLoaded) {
return <Text>...Loading</Text>;
} else {
return (
<View style={containerStyle(height, width).container}>
<ImageBackground
resizeMode={"cover"}
style={styles.image}
source={require("../assets/model.jpg")}
>
<LinearGradient
colors={["#00000000", "#000000"]}
style={styles.gradient}
>
<View style={styles.container}>
<View style={styles.logoTextContainer}>
<Text style={styles.logoText}>{title}</Text>
<Text style={styles.logoText}>{subTitle}</Text>
</View>
<ContinueBtn label={btnText} callback={continueBtn} />
</View>
</LinearGradient>
</ImageBackground>
</View>
);
}
};
AppNavigator'ımda kullanıcının hangi navigasyonu görmesi gerektiğine karar vermek istiyorum. Ancak devam et butonuna bastığımda uygulamam TabsNavigator'ıma gitmiyor. Onboarding sayfamda kalıyor ama uygulamayı yenilersem uygulama TabsNavigator'ıma gidiyor. İşte kullanıcının yeni mi yoksa "eski" bir kullanıcı mı olduğuna bağlı olarak nerede olması gerektiğini belirlediğim kod:
const WelcomeScreen: FC<IWelcomeScreen> = ({ navigation }) => {
const { width, height } = Dimensions.get("window");
const btnText = "Contiunue";
const title = "Book";
const subTitle = "Fab";
let [fontsLoaded] = useFonts({
PinyonScript_400Regular,
});
const continueBtn = async () => {
try {
await AsyncStorage.setItem('@viewedOnboarding', 'true');
} catch (error) {
console.log('Error @setItem: ', error);
};
};
if (!fontsLoaded) {
return <Text>...Loading</Text>;
} else {
return (
<View style={containerStyle(height, width).container}>
<ImageBackground
resizeMode={"cover"}
style={styles.image}
source={require("../assets/model.jpg")}
>
<LinearGradient
colors={["#00000000", "#000000"]}
style={styles.gradient}
>
<View style={styles.container}>
<View style={styles.logoTextContainer}>
<Text style={styles.logoText}>{title}</Text>
<Text style={styles.logoText}>{subTitle}</Text>
</View>
<ContinueBtn label={btnText} callback={continueBtn} />
</View>
</LinearGradient>
</ImageBackground>
</View>
);
}
};
Cevap #1
Async storage'da bir değer ayarlamak, AppNavigator'ın yeniden render edilmesini tetiklemez. Bu nedenle, kullanıcı devam düğmesine bastığında, AppNavigator durumu değişmediği için görsel olarak hiçbir şey olmayacaktır. Uygulamayı yenilerseniz, daha önce setItem fonksiyonunu kullanarak ayarladığınız bayrak, ilk render sırasında AppNavigator'da yeniden yüklenecektir. Bu, uygulamayı yeniledikten sonra neden çalıştığının nedenidir.
Bu tür bir problem için, AppNavigator'da bir durum değişikliğini tetiklemek için bir Context kullanmanızı öneririm.
İşte bunun nasıl çalışacağına dair minimal bir örnek. Size rehberlik etmesi için koda yorumlar ekledim.
Basitlik adına, aşağıdaki varsayımı yapacağız:
Bir Stack içinde iki ekranımız var, biri WelcomeScreen, diğeri ise HomeScreen olarak adlandırılıyor.
Uygulama bağlamımıza bağlı olarak ekranlar için koşullu render kullandığımıza dikkat edin. İstediğiniz herhangi bir ekranı ekleyebilirsiniz, hatta tüm navigatörleri bile (navigatörleriniz iç içe geçmişse bu gerekli olacaktır, ancak desen aynı kalır).
Uygulama
export const AppContext = React.createContext()
const App = () => {
// it is important that the initial state is undefined, since
// we need to wait for the async storage to return its value
// before rendering anything
const [hasViewedOnboarding, setHasViewedOnboarding] = React.useState()
const appContextValue = useMemo(
() => ({
hasViewedOnboarding,
setHasViewedOnboarding,
}),
[hasViewedOnboarding]
)
// retrieve the onboarding flag from the async storage in a useEffect
React.useEffect(() => {
const init = async () => {
const value = await AsyncStorage.getItem('@viewedOnboarding')
setHasViewedOnboarding(value != null ? JSON.parse(value) : false)
}
init()
}, [])
// as long as the flag has not been loaded, return null
if (hasViewedOnboarding === undefined) {
return null
}
// wrap everything in AppContext.Provider an pass the context as a value
return (
<AppContext.Provider value={appContextValue}>
<NavigationContainer>
<Stack.Navigator>
{!hasViewedOnboarding ? (
<Stack.Screen name="Welcome" component={WelcomeScreen} />
) : (
<Stack.Screen
name="Home"
component={HomeScreen}
/>
)}}
</Stack.Navigator>
</NavigationContainer>
</AppContext.Provider>
)
}
Şimdi, WelcomeScreen içinde bağlama erişmeniz ve asenkron değer saklandıktan sonra durumu ayarlamanız gerekiyor.
const WelcomeScreen: FC<IWelcomeScreen> = ({ navigation }) => {
// access the context
const { setHasViewedOnboarding } = useContext(AppContext)
const { width, height } = Dimensions.get("window");
const btnText = "Contiunue";
const title = "Book";
const subTitle = "Fab";
let [fontsLoaded] = useFonts({
PinyonScript_400Regular,
});
const continueBtn = async () => {
try {
await AsyncStorage.setItem('@viewedOnboarding', 'true');
setHasViewedOnboarding(true)
} catch (error) {
console.log('Error @setItem: ', error);
};
};
if (!fontsLoaded) {
return <Text>...Loading</Text>;
} else {
return (
<View style={containerStyle(height, width).container}>
<ImageBackground
resizeMode={"cover"}
style={styles.image}
source={require("../assets/model.jpg")}
>
<LinearGradient
colors={["#00000000", "#000000"]}
style={styles.gradient}
>
<View style={styles.container}>
<View style={styles.logoTextContainer}>
<Text style={styles.logoText}>{title}</Text>
<Text style={styles.logoText}>{subTitle}</Text>
</View>
<ContinueBtn label={btnText} callback={continueBtn} />
</View>
</LinearGradient>
</ImageBackground>
</View>
);
}
};
