i am facing a problem regarding async storage in react native .
when i setItem in async storage and then retrieve it if there are 3 tasks- which i have added,
only two of them is retrieved
i will share a photo of before and after
this is output before refreshing
this is the output after refreshing the app
This is my app.js code
import { StatusBar } from 'expo-status-bar';
import {
StyleSheet,
Text,
View,
KeyboardAvoidingView,
FlatList,
TextInput,
TouchableOpacity,
Keyboard,
} from 'react-native';
import React, { Component } from 'react';
import * as Font from 'expo-font';
import Task from './components/Task';
import AppLoading from 'expo-app-loading';
import AsyncStorage from '@react-native-async-storage/async-storage';
let customFonts = {
Poppins_SemiBold: require('./assets/Poppins-SemiBold.ttf'),
Poppins_Regular: require('./assets/Poppins-Regular.ttf'),
};
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
task: '',
taskItems: [],
fontsLoaded: false,
};
}
async _loadFontsAsync() {
await Font.loadAsync(customFonts);
this.setState({ fontsLoaded: true });
}
componentDidMount() {
this._loadFontsAsync();
this._retrieveData()
}
_retrieveData = async () => {
try {
const value = await AsyncStorage.getItem('data');
if (value.length !== 2) {
// We have data!!
this.setState({taskItems:[...JSON.parse(value)]})
console.log(value);
}
} catch (error) {
// Error retrieving data
console.log(error)
}
};
handleAddTask=()=>{
Keyboard.dismiss()
this.setState({taskItems:[...this.state.taskItems,this.state.task]})
this.setState({task:''})
AsyncStorage.setItem('data',JSON.stringify(this.state.taskItems))
}
deleteItem=(index)=>{
try {
let arr = [...this.state.taskItems];
arr.splice(index, 1);
this.setState({taskItems:arr})
AsyncStorage.setItem('data',JSON.stringify(arr))
} catch (err) {
console.log(err);
}
}
render() {
if (!this.state.fontsLoaded) {
return <AppLoading />;
}
return (
<View style={styles.container}>
{/* Todays Tasks */}
<View style={styles.taskWrapper}>
<Text style={styles.sectionTitle}>Today's Tasks</Text>
<View style={styles.items}>
{/* This is where the tasks will go! */}
<FlatList
data={this.state.taskItems}
keyExtractor={(item) => item}
renderItem={({ item, index }) => (
<Task text={item} handleDelete={() => this.deleteItem(index)} />
)}
/>
</View>
</View>
{/* Write a Task */}
<KeyboardAvoidingView style={styles.writeTaskWrapper}>
<TextInput
style={styles.input}
placeholder={'Write A Task!'}
onChangeText={(text) => {
this.setState({ task: text });
}}
value={this.state.task}
/>
<TouchableOpacity
onPress={() => {
this.handleAddTask();
}}>
<View style={styles.addWrapper}>
<Text style={styles.addText}>+</Text>
</View>
</TouchableOpacity>
</KeyboardAvoidingView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#E8EAED',
},
taskWrapper: {
paddingTop: 80,
paddingHorizontal: 20,
},
sectionTitle: {
fontSize: 24,
backgroundColor: '#fff',
fontFamily: 'Poppins_SemiBold',
borderRadius: 10,
margin: 'auto',
width: 250,
height: 60,
textAlign: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.2,
shadowRadius: 2,
elevation: 5,
paddingTop: 10,
},
items: {
marginTop: 30,
},
writeTaskWrapper: {
position: 'absolute',
bottom: 60,
width: '100%',
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
},
input: {
paddingVertical: 15,
paddingHorizontal: 15,
backgroundColor: '#fff',
borderRadius: 60,
width: 250,
fontFamily: 'Poppins_Regular',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.5,
shadowRadius: 2,
elevation: 3,
},
addWrapper: {
width: 60,
height: 60,
backgroundColor: '#fff',
borderRadius: 60,
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.5,
shadowRadius: 2,
elevation: 3,
},
addText: {},
});
and this is my task.js code which is a component:
import React from 'react';
import {
View,
Text,
StyleSheet,
Dimensions,
Animated,
TouchableOpacity,
} from 'react-native';
import AppLoading from 'expo-app-loading';
import {
Poppins_100Thin,
Poppins_100Thin_Italic,
Poppins_200ExtraLight,
Poppins_200ExtraLight_Italic,
Poppins_300Light,
Poppins_300Light_Italic,
Poppins_400Regular,
Poppins_400Regular_Italic,
Poppins_500Medium,
Poppins_500Medium_Italic,
Poppins_600SemiBold,
Poppins_600SemiBold_Italic,
Poppins_700Bold,
Poppins_700Bold_Italic,
Poppins_800ExtraBold,
Poppins_800ExtraBold_Italic,
Poppins_900Black,
Poppins_900Black_Italic,
} from '@expo-google-fonts/poppins';
import { useFonts } from 'expo-font';
import Swipeable from 'react-native-gesture-handler/Swipeable';
const SCREEN_WIDTH = Dimensions.get('window').width;
const Task = (props) => {
let [fontsLoaded, error] = useFonts({
Poppins_100Thin,
Poppins_100Thin_Italic,
Poppins_200ExtraLight,
Poppins_200ExtraLight_Italic,
Poppins_300Light,
Poppins_300Light_Italic,
Poppins_400Regular,
Poppins_400Regular_Italic,
Poppins_500Medium,
Poppins_500Medium_Italic,
Poppins_600SemiBold,
Poppins_600SemiBold_Italic,
Poppins_700Bold,
Poppins_700Bold_Italic,
Poppins_800ExtraBold,
Poppins_800ExtraBold_Italic,
Poppins_900Black,
Poppins_900Black_Italic,
});
if (!fontsLoaded) {
return <AppLoading />;
}
const leftSwipe = (progress, dragX) => {
const scale = dragX.interpolate({
inputRange: [0, 100],
outputRange: [0, 1],
extrapolate: 'clamp',
});
return (
<TouchableOpacity onPress={props.handleDelete} activeOpacity={0.6}>
<View style={styles.deleteBox}>
<Animated.Text
style={{
transform: [{ scale: scale }],
color: '#fff',
fontFamily: 'Poppins_400Regular',
fontSize: 18,
}}>
Delete
</Animated.Text>
</View>
</TouchableOpacity>
);
};
return (
<Swipeable renderLeftActions={leftSwipe}>
<View style={styles.item}>
<View style={styles.itemLeft}>
<View style={styles.square}></View>
<Text style={styles.itemText}>{props.text}</Text>
</View>
<View style={styles.circular}></View>
</View>
</Swipeable>
);
};
const styles = StyleSheet.create({
item: {
backgroundColor: 'white',
padding: 15,
borderRadius: 10,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 20,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.5,
shadowRadius: 2,
elevation: 3,
},
itemLeft: {
flexDirection: 'row',
alignItems: 'center',
flexWrap: 'wrap',
},
square: {
width: 24,
height: 24,
backgroundColor: '#55BCF6',
opacity: 0.5,
borderRadius: 5,
marginRight: 15,
},
itemText: {
maxWidth: '80%',
fontFamily: 'Poppins_400Regular',
},
circular: {
width: 12,
height: 12,
borderColor: '#55BCF6',
borderWidth: 2,
borderRadius: 5,
},
deleteBox: {
backgroundColor: 'red',
justifyContent: 'center',
alignItems: 'center',
width: 100,
height: 55,
borderRadius: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.5,
shadowRadius: 2,
elevation: 5,
},
});
export default Task;
Due to React internal state update implementation, update Async storage run before new state updated. It's recommended to run asynchronous code before updating the state.
To solve the issue, refactor your code as below:
handleAddTask= async ()=>{
Keyboard.dismiss()
const updatedTaskItems = [...this.state.taskItems,this.state.task]
await AsyncStorage.setItem('data',JSON.stringify(updatedTaskItem))
this.setState({taskItems:updatedTaskUtems,task:''})
}