Skip to content

πŸ§™β€β™‚οΈ Working with custom hooks πŸ§™β€β™‚οΈ

Notifications You must be signed in to change notification settings

ch0ripain/react-custom-hooks

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

22 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ§™β€β™‚οΈ Custom Hooks πŸ§™β€β™‚οΈ

A custom hook is simply a normal JS function whose purpose is to wrap all the state/logic which is closely-related and repetitive to use it wherever you want avoiding that boilerplate code and encouraging reusability.

In this project, i used a custom hook to group up a fetch feature that manages 3 related states and some logic

  //states
  const [userPlaces, setUserPlaces] = useState([]);
  const [isFetching, setIsFetching] = useState(false);
  const [error, setError] = useState();
  //logic
  useEffect(() => {
    async function fetchPlaces() {
      setIsFetching(true);
      try {
        const places = await fetchUserPlaces();
        setUserPlaces(places);
      } catch (error) {
        setError({ message: error.message || 'Failed to fetch user places.' });
      }
      setIsFetching(false);
    }
    fetchPlaces();
  }, []);

Creating the Custom Hook πŸ”§

I organized the project by adding a hooks folder and a useFetch.js file.

  • folder ➑️ can also be named customhooks or anything descriptive.
  • file ➑️ name must start with use to comply and take advantage of React Hooks Rules
export function useFetch(fetchFn,initialValue){
  //modified states
  const [isFetching, setIsFetching] = useState(false);
  const [data, setData] = useState(initialValue);
  const [error, setError] = useState();
  //modified logic
  useEffect(() => {
    async function fetchData() {
      setIsFetching(true);
      try {
        const data = await fetchFn();
        setData(places);
      } catch (error) {
        setError({ message: error.message || 'Failed to fetch data.' });
      }
      fetchData(false);
    }
    fetchPlaces();
  }, [fetchFn]);

  return {
  isFetching,
  data,
  setData,
  error
}
  • Generalize state/logic ➑️ state and logic variables names are now more generic.
  • Flexible Parameters ➑️ added fetchFn and initialValue to allow dynamic usage.
  • Effect Dependencies ➑️ ensured fetchFn is listed as a dependency for proper reusability.
  • Reusability ➑️ returns an object containing all values and functions to expose.

Using the Custom Hook πŸ”„

Now, this custom hook can be used as follows:

  import { useFetch } from "./hooks/useFetch.js";
  const { isFetching, data: userPlaces, error } = useFetch(fetchUserPlaces, []);
  ...
  <DummyComponent isLoading={isFetching} places={userPlaces} />
  {error && <Error title="An error occurred!" message={error.message} />}

Another Use Case: Nested Logic in Fetch 🎩

In AvailablePlaces.jsx, I needed to fetch available places and sort them by user location using the navigator API:

 useEffect(() => {
          ...
                navigator.geolocation.getCurrentPosition((position) => {
                const sortedPlaces = sortPlacesByDistance(
                  places,
                  position.coords.latitude,
                  position.coords.longitude
                );
          ...
    }

To integrate this with the custom hook, a customized fetch function was needed:

  //create a function with all the nested behavior what is needed
   async function fetchSortedPlaces(){
   const places = await fetchAvailablePlaces() // first retrieve all that places
   //then returns a Promise with the resolve (sortedPlaces) or reject (not handled in this case)
   return new Promise((resolve,reject) => {
   navigator.geolocation.getCurrentPosition((position) => {
                const sortedPlaces = sortPlacesByDistance(
                  places,
                  position.coords.latitude,
                  position.coords.longitude
                );
                resolve(sortedPlaces)
   })
   })
   }

Now, use the custom hook with the fetch function

 const { isFetching, data: availablePlaces, error } = useFetch(fetchSortedPlaces, []);

Quick Recap πŸ”„

  • Create a reusable custom hook file (useFn.js) to manage some closely-related state/logic.
  • Generalize the state/logic and also add parameters for flexibility.
  • Handle other use cases with custom functions (async/Promise).

Finally, the project is cleaner and the custom hook can be easily reused across components as it is unique to each component's use.


🐸 This project is a practice exercise I learned from the Academind's React Course 🐸

About

πŸ§™β€β™‚οΈ Working with custom hooks πŸ§™β€β™‚οΈ

Topics

Resources

Stars

Watchers

Forks