react-nativereact-navigationreact-native-flatlistreact-navigation-bottom-tab

How can I refresh FlatList from pressing bottom bar tab?


I'm not able to find a correct way to refresh a FlatList when bottom bar tab is pressed and that we are at the top of the FlatList.

I did succeed to reach the top of the FlatList using useScrollToTop, also I managed to refetch data but it is not as I would like to be.

import { useScrollToTop } from '@react-navigation/native';

const flatlistRef = useRef<FlatList>(null);
useScrollToTop(flatlistRef);

This scroll to the top of the FlatList but :

  1. I want to refetch only if top is already reached when pressing bottom bar tab
  2. Using this I don't have the refresh control animation to see that something is refetching (it's just refetching underground)

I'm using React-Navigation for bottom tabs ("@react-navigation/bottom-tabs": "^6.5.16")

Any idea?

Thanks


Solution

  • Since you want to do more than just scroll to the top when the tab is focused, you should give up on using useScrollToTop and just use useFocusEffect. The hook is called whenever the current tab comes into focus. You can both refresh the data and scroll to the top of the flatlist in this hook (demo). To refresh when home screen is pressed is add a navigation listener for tab presses and then refresh there:

    import { useState, useEffect, useCallback, useRef } from 'react';
    import {
      View,
      Text,
      StyleSheet,
      FlatList,
      ActivityIndicator,
      RefreshControl as RNRefreshControl,
      Platform
    } from 'react-native';
    import { RefreshControl as WebRefreshControl } from 'react-native-web-refresh-control'
    import { useFocusEffect } from '@react-navigation/native';
    import useCountSeconds from './useCountSeconds';
    import ListItem from './ListItem';
    const RefreshControl = Platform.OS === 'web'
      ? WebRefreshControl
      :RNRefreshControl
    export default function HomeScreen({ navigation }) {
      const [data, setData] = useState([]);
      const [isLoading, setIsLoading] = useState(false);
      const flatlistRef = useRef();
      // counts seconds between lastRefresh and current time
      const { seconds, lastRefresh } = useCountSeconds(Date.now());
      const refreshData = useCallback(async () => {
        setIsLoading(true);
        try {
          // get sample data
          const data = await fetch('https://dummyjson.com/products');
          const json = await data.json();
          setData(json.products);
          // update lastRefresh
          lastRefresh.current = Date.now();
          setIsLoading(false);
        } catch (err) {
          console.log(err);
          setIsLoading(false);
        }
      }, [lastRefresh]);
    
      useFocusEffect(
        // must wrap function in callback to avoid
        // calling this effect alot
        useCallback(() => {
          console.log('Home screen focused');
          refreshData();
          flatlistRef.current?.scrollToOffset({ offset: 0 });
        }, [refreshData])
      );
      // listen for Home tab presses
      useEffect(()=>{
        const unsubscribe = navigation.addListener('tabPress',e=>{
          if(e.target.includes('Home')){
            console.log(e)
            refreshData()
          }
        })
        return ()=>{
          unsubscribe()
        }
      },[navigation,refreshData])
      return (
        <View style={styles.container}>
          <Text>Refreshed: {seconds}s ago</Text>
          <View style={styles.listContainer}>
            <FlatList
              contentContainerStyle={{paddingTop:Platform.select({
                web:20,
                default:0
              })}}
              ref={flatlistRef}
              data={data}
              renderItem={(props) => <ListItem {...props} />}
              keyExtractor={(item) => item.id}
              numColumns={2}
              refreshControl={
                <RefreshControl refreshing={isLoading} onRefresh={refreshData} />
              }
            />
          </View>
        </View>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        width: '100%',
        alignItems: 'center',
        justigyContent: 'center',
      },
      listContainer: {
        flex: 1,
        width: '100%',
        margin: 10,
        padding: 5,
      },
    });