How to reduce the number of requests in React Navigation TopTabNavigator using expo?

124 views Asked by At

i'm pretty new on React Native and currently i am developing an app using expo. I am using TopTabNavigator from react navigation 6 and i don't understand how to reduce the no of the requests. Basically, whenever i hit a certain tab the request is made. (because the component is recreated - even if i come back on previous tab which is the same, no data modified). I tried to use useFocusEffect from react navigation but it does not work as i expected. Maybe i should make the requests in the ProfileTabsScreen and pass the data via props to the particular tabs?

MAIN COMPONENT

const ProfileStatsScreen = (props) => {
  const { userId } = props.route.params;
  const { initialRoute, username } = props.route.params;

  const RatingsDetails = () => <RatingsTab userId={userId} />;
  const FollowersDetails = () => <FollowersTab userId={userId} />;
  const FollowingsDetails = () => <FollowingsTab userId={userId} />;

  return (
    <SafeAreaView style={styles.screen}>
      <Header title={username} />
      <TopTabContainer initialRouteName={initialRoute}>
        <Tab.Screen
          name="Ratings"
          component={RatingsDetails}
          options={{ tabBarLabel: "Reviews" }}
        />
        <Tab.Screen
          name="Followers"
          component={FollowersDetails}
          options={{ tabBarLabel: "Followers" }}
        />
        <Tab.Screen
          name="Following"
          component={FollowingsDetails}
          options={{ tabBarLabel: "Followings" }}
        />
      </TopTabContainer>
    </SafeAreaView>
  );
};

TAB COMPONENT (RATINGS)

export const RatingsTab = ({ userId }) => {
  const { user } = useAuth();
  const [reviews, setReviews] = useState([]);
  const [loading, setLoading] = useState(false);

  useFocusEffect(
    React.useCallback(() => {
      setLoading(true);
      axios
        .get(`${process.env.BASE_ENDPOINT}/users/${userId}/reviews`, {
          headers: { Authorization: `Bearer ${user?.token}` },
        })
        .then((res) => {
          setReviews(res.data.reviews);
          setLoading(false);
        })
        .catch((err) => {
          console.log(err);
          setLoading(false);
        });
      setLoading(false);
    }, [userId, user?.token])
  );

  const renderRatings = ({ item }) => {
    const { reviewer, rating, review, createdAt } = item;

    return (
      <CardRatings
        avatar={reviewer?.avatar}
        name={reviewer?.name}
        date={moment(createdAt).format("LL")}
        rating={rating}
        review={review}
        service={"Tuns"}
      />
    );
  };

  return (
    <>
      {!loading && (
        <FlatList
          data={reviews}
          keyExtractor={(item) => item?._id}
          renderItem={renderRatings}
        />
      )}
      {loading && <Spinner />}
    </>
  );
};
2

There are 2 answers

7
Radu On

Thank you, but unfortunately is not working. Let's assume that i am already in the RatingsTab and i have the data because by far the request was already made. If i go to FollowersTab and after that i come back to RatingsTab i don't want to make call if the data has not changed. if (isLoading) return; i think it will not helping me, because the loading state is false at first (when the Ratings Tab component is re-created).

0
Abe On

You're very close to the solution, your useFocusEffect is configured properly. Change the lines

useFocusEffect(
    React.useCallback(() => {
      setLoading(true);

to read

useFocusEffect(
    React.useCallback(() => {
      if (isLoading) return; 
      setLoading(true);

i.e., if loading is true, don't make the axios call. While this doesn't eliminate the possibility of extra requests, it should reduce what you're seeing by quite a bit.

Also, since you're using .then, wrap the final line of your callback in .finally.

.finally(()=> {
  setLoading(false)
});

Otherwise, your loading state will be set to false before the promise resolves.