Home > Enterprise >  React Native: data not displaying after async fetch
React Native: data not displaying after async fetch

Time:03-29

I'm developing a mobile app with React Native and Expo managed workflow. The app is supposed to serve as a song book with lyrics to songs and hymns. All of the lyrics are stored in Firebase' Firestore database and clients can load them in app. I started to implement offline functionality, where all of the lyrics are stored on the user's device using community's AsyncStorage.

I want to get the data stored in AsyncStorage first, set them to state variable holding songs and then look if user has internet access. If yes, I want to check for updates in Firestore, if there were any, I will set the data from Firestore to state variable holding songs. If user does not have internet access, the data from AsyncStorage will already be set to state variable holding songs.

I'm trying to achieve this with an async function inside useEffect hook with empty array of vars/dependecies. The problem I'm having is that no songs are rendered on screen even though they are successfuly retrieved from AsyncStorage.

(When I console.log the output of retrieving the data from AsyncStorage I can see all songs, when I console log songs or allSongs state var, I'm getting undefined)

Here is my simplified code:

import React, { useEffect, useState } from 'react';
import {
  StyleSheet,
  FlatList,
  SafeAreaView,
  LogBox,
  View,
  Text,
} from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { filter, _ } from 'lodash';
import { doc, getDoc } from 'firebase/firestore';
import NetInfo from '@react-native-community/netinfo';

import { db } from '../../../firebase-config';
import { ThemeContext } from '../../util/ThemeManager';
import {
  getStoredData,
  getStoredObjectData,
  storeData,
  storeObjectData,
} from '../../util/LocalStorage';

const SongsList = ({ route, navigation }) => {

  // const allSongs = props.route.params.data;
  const { theme } = React.useContext(ThemeContext);
  const [loading, setLoading] = useState(false);
  const [allSongs, setAllSongs] = useState();
  const [songs, setSongs] = useState(allSongs);
  const hymnsRef = doc(db, 'index/hymns');

  useEffect(() => {
    const setup = async () => {
      setLoading(true);
      const locData = await getStoredObjectData('hymnsData');
      console.log(locData);
      setAllSongs(locData);
      setSongs(locData);
      const netInfo = await NetInfo.fetch();
      if (netInfo.isInternetReachable) {
        const data = await getDoc(hymnsRef);
        const lastChangeDb = data.get('lastChange').valueOf();
        const hymnsData = data.get('all');
        const lastChangeLocal = await getStoredData('lastChange');
        if (lastChangeLocal) {
          if (lastChangeLocal !== lastChangeDb) {
            await storeData('lastChange', lastChangeDb);
            await storeObjectData('hymnsData', hymnsData);
            setAllSongs(hymnsData);
            setSongs(hymnsData);
          }
        }
      }
      sortHymns();
      setLoading(false);
    };
    setup();
  }, []);

  return (
    <SafeAreaView style={[styles.container, styles[`container${theme}`]]}>
      {!loading ? (
        <FlatList
          data={songs}
          keyExtractor={(item) => item?.number}
          renderItem={({ item }) => {
            return <ListItem item={item} onPress={() => goToSong(item)} />;
          }}
          ItemSeparatorComponent={Separator}
          ListHeaderComponent={
            route.params.filters ? (
              <SearchFilterBar
                filters={filters}
                handleFilter={handleFilter}
                query={query}
                handleSearch={handleSearch}
                seasonQuery={seasonQuery}
                setSeasonQuery={setSeasonQuery}
              />
            ) : (
              <SearchBar handleSearch={handleSearch} query={query} />
            )
          }
        />
      ) : (
        <View>
          <Text>loading</Text>
        </View>
      )}
      <StatusBar style={theme === 'dark' ? 'light' : 'dark'} />
    </SafeAreaView>
  );
};

export default SongsList;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

The functions getStoredData, getStoredObjectData, storeData and storeObjectData are just getItem and setItem methods of AsyncStorage.

Here is my full code - GitHub.

What am I doing wrong? I've went over many tutorials and articles and it should be working... but I guess not?

CodePudding user response:

Can you check if hymnsData is undefined? After the const hymnsData = data.get('all'); line.

If so, that would explain the issue - you are correctly setting the locData but then overwriting it immediately after. If that is the case, I would add hymnsData to the if condition if (hymnsData && lastChangeLocal) { ... }

If you log songs and allSongs right before the return (, do you see ever see that they are populated, briefly?

Another thing I'd do to debug, is comment out the

setAllSongs(hymnsData);
setSongs(hymnsData);

lines and see if it is working as expected with locData only

CodePudding user response:

The problem was with the sortHymns() method. I moved it from it's own method to the useEffect and it's working now.

  • Related