Home > Mobile >  React - Refactoring logic with hooks
React - Refactoring logic with hooks

Time:10-20

INTRODUCTION

I have a screen which makes some queries to my server:

  1. Fetch user by username (when the user types something in the search input)

  2. Fetch premium users (when screen mounts pull to refresh)

  3. Fetch young users (when screen mounts pull to refresh)

I am thinking about moving this to a hook.

This is my current code:

function MyScreen() {
   // Fetch by username
   const [isSearching, setIsSearching] = useState([]);
   const [searchedUsers, setSearchedUsers] = useState([]);


   // Premium
   const [isLoading, setIsLoading] = useState(true); <--- NOTE THIS 
   const [premiumUsers, setPremiumUsers] = useState([]);

   // Young users
   const [youngUsers, setYoungUsers] = useState([]);


   /*
       FIRST METHOD
   */
   const searchUsersByUsername = async (limit = 20) => {
      setIsSearching(true);

      try {
         const result = await api.users.searchUsersByUsername(username, limit);
         setSearchedUsers(result);
      } catch(err) {
         console.log(err);
      }

      setIsSearching(false);
   }

   /*
       SECOND METHOD
   */
   const getPremiumUsers = async (limit = 10) => {
      // Note: No loading here
      try {
         const result = await api.users.getPremiumUsers(limit);
         setPremiumUsers(result);
      } catch(err) {
         console.log(err);
      }
   }


   /*
       THIRD METHOD
   */
   const getYoungUsers = async (limit = 10) => {
      // Note: No loading here
      try {
         const result = await api.users.getYoungUsers(limit);
         setYoungUsersUsers(result);
      } catch(err) {
         console.log(err);
      }
   }



   // Effects and rendering...   

   useEffect(() => {
      (async () => {
         const promises = [getPremiumUsers(), getYoungUsers()];

         await Promise.all(promises).catch((err) => {
            console.log(err))
         });

         setIsLoading(false); // <---- NOTE THIS
      })();
      
   }, []);      

}

PROBLEM

I cannot use the typical useQuery hook, as I am using Firestore Callable Functions. So, the only way to make requests, is to call my api methods (api.users.searchUser...)

As you can see in the code, I have two types of loading indicators (two states):

  1. isSearching (for the searching by username functionality)

  2. isLoading (for fetching premium and young users in parallel)

How can I implement a reusable hook for this logic?

I mean, I need to abstract all this stuff, in order to be able to:

  1. Search users by username in other screens

  2. Search premium users (without fetching young users in parallel)

  3. Search young users (without fetching premium users in parallel)

And, also, to get the loading status of the three queries.

Note: In my current screen, as I have said before, I am using "setIsLoading" for the young and premium users parallel fetching, but maybe (to be more flexible) in other screens I will need the loading status for each logic independently.

Any help or ideas?

CodePudding user response:

You could use a React.useEffect and React.useCallback by each fetch method. Also, save the loading status individually.

Check this out:

import { useEffect, useCallback } from 'react';

const defaultParams = {
  fetchPremiumUsersOnMount: false,
  fetchYoungUsersOnMount: false,
  searchOnMount: false,
  username: '',
  limit: 20,
}

function useUsersApi(params = defaultParams) {
   const [isSearching, setIsSearching] = useState(false);
   const [searchedUsers, setSearchedUsers] = useState([]);

   const [premiumUsers, setPremiumUsers] = useState([]);
   const [premiumLoading, setPremiumLoading] = useState(false);

   const [youngUsers, setYoungUsers] = useState([]);
   const [youngLoading, setYoungLoading] = useState(false);

  const fetchPremiumUsers = useCallback(async () => {
    try {
      setPremiumLoading(true);
      const result = api.users.getPremiumUsers(params.limit);
      setPremiumUsers(result);
    } catch (err) {
      console.log(err)
    } finally {
      setPremiumLoading(false);
    }
  }, [params.limit]);

  const fetchYoungUsers = useCallback(async () => {
    /* similar logic to `fetchPremiumUsers` */
  }, [params.limit, params.]);

  const fetchSearchUsers = useCallback(async (username) => {
    /* fetch logic here */
  }, [params.limit]);

   useEffect(() => {
     if(params.fetchPremiumUsersOnMount) {
       fetchPremiumUsers();
     }
   }, [params.fetchPremiumUsersOnMount, params.limit]); 


   useEffect(() => {
     if(params.fetchYoungUsersOnMount) {
       fetchYoungUsers();
     }
   }, [params.fetchYoungUsersOnMount, params.limit]); 

   useEffect(() => {
     if(params.fetchSearchUsers) {
       fetchSearchUser(params.username);
     }
   }, [params.searchOnMount, params.limit, params.username]); 


  return {
    isSearching,
    searchedUsers,
    isLoading: premiumLoading || youngLoading,
    premiumUsers,
    premiumUsersLoading: premiumLoading,
    refreshPremiumUsers: fetchPremiumUsers,
    youngUsers,
    youngUsersLoading: youngLoading,
    refreshYoungUsers: fetchYoungUsers,
  }
}
  • Related