skip to Main Content

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

2

Answers


  1. Chosen as BEST ANSWER

    Found a solution to this — might be better answer if so feel free to respond!

    const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
    const flatlistRef = useRef<FlatList>(null);
    const scrollPositionRef = useRef(0);
    
    const refetchControl = async () => {
        setIsRefreshing(true);
        await refetchAPIEndpoint();
        setIsRefreshing(false);
    };
    
    const handleScroll = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
        scrollPositionRef.current = event.nativeEvent.contentOffset.y;
    };
    
    const scrollToTop = async () => {
        if (scrollPositionRef.current === 0) {
            await refetchControl();
            flatlistRef.current?.scrollToOffset({ offset: 0 });
        } else {
            flatlistRef.current?.scrollToOffset({ offset: 0 });
        }
    }
    
    useScrollToTop(
        useRef({ scrollToTop }),
    );
    
    return (
        <FlatList
            ...
            ref={flatlistRef}
            onScroll={handleScroll}
            refreshControl={<RefreshControl refreshing={isRefreshing} onRefresh={refetchControl} />}
        />
    );
    

    Didn't really understand useScrollToTop(useRef({ scrollToTop })) this, but it works — feel free to explain!


  2. 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):

    import { useState, useEffect, useCallback, useRef } from 'react';
    import {
      View,
      Text,
      StyleSheet,
      FlatList,
      ActivityIndicator,
    } from 'react-native';
    import { useFocusEffect } from '@react-navigation/native';
    import useCountSeconds from './useCountSeconds';
    import ListItem from './ListItem';
    
    export default function HomeScreen({ navigation }) {
      const [data, setData] = useState([]);
      const [isLoading, setIsLoading] = useState(false);
      const flatlistRef = useRef();
      // debug value used to show when last fetch was made
      const [lastRefresh, setLastRefresh] = useState(Date.now());
      // counts seconds between lastRefresh and current time
      const seconds = useCountSeconds(lastRefresh);
      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
          setLastRefresh(Date.now());
          setIsLoading(false);
        } catch (err) {
          console.log(err);
          setIsLoading(false);
        }
      }, []);
    
      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])
      );
    
      return (
        <View style={styles.container}>
          {isLoading && <ActivityIndicator size="large" />}
          <Text>Refreshed: {seconds}s ago</Text>
          <View style={styles.listContainer}>
            <FlatList
              ref={flatlistRef}
              data={data}
              renderItem={(props) => <ListItem {...props} />}
              keyExtractor={(item) => item.id}
              numColumns={2}
            />
          </View>
        </View>
      );
    }
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: 'center',
        justigyContent: 'center',
      },
      listContainer: {
        flex: 1,
        margin: 10,
        padding: 5,
      },
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search