Search code examples
javascriptreact-nativeasyncstorage

i am facing a problem regarding async storage in react native


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;

Solution

  • 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:''})
        
      }