Search code examples
javascriptreact-nativelistviewasyncstorage

Last AsyncCallback item disappearing in React Native


I have an array of objects and this data should be stored in when new item is appended to the list. Also, when the application loads, this information have to be pushed back into the list. So, I use AsyncCallback, but it doesn't work properly. When I refresh application, all stored elements appear in the list except the last one. How can I return this item back to the list?

import React, { useState, useEffect } from 'react';
import { StyleSheet, View, Text, FlatList, Button, AsyncStorage } from 'react-native';

export default function HomeScreen() {

  const [listItems, setListItems] = useState([
  {
    // example list item
    id: '0',
    text: 'Item 1'
  },
  ]);
  const [idx, incrIdx] = useState(1);

  useEffect(() => {
    getData();
  }, []);


  const pushItem = () => {
    var data = new Object();
    data.id = idx.toString();
    data.text = "Item " + idx;
    setListItems([...listItems, data]);
    incrIdx(idx + 1);
    storeData();
  };

  const storeData = async () => {
      await AsyncStorage.setItem('listItems', JSON.stringify(listItems));
  };

  const getData = async () => {
    const value = await AsyncStorage.getItem('listItems');
    if(value !== null) {
      setListItems([...listItems, JSON.parse(value)]);
    }
  };

  return (
    <View style={styles.container}>
      <FlatList
          data={listItems}
          renderItem={({item}) => 
            <View>
              <Text>{item.text}</Text>
            </View>
          }
          keyExtractor={item => item.id}
      />
      <Button
        title="Push data"
        onPress={pushItem}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
});

Solution

  • Did you look at what AsyncStorage.setItem is actually setting?

    My guess is that it's not using the latest listItems state because hook setters are asynchronous (just like the old setState).

    To express logic that should take effect when state is updated, use the useEffect api.

    useEffect(() => {
      // Instead of calling storeData() with stale state
      AsyncStorage.setItem('listItems', JSON.stringify(listItems));
    }, [listItems]);