Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: How optimize a custom hook that shares data?

I have a Custom Hook similarly with the below:

import { useEffect, useState } from 'react';

import axios from 'axios';

const myCustomHook = () => {
  const [countries, setCountries] = useState([]);
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {
      (async () =>
        await axios
          .get("MY_API/countries")
          .then(response => setCountries(response.data))
          .finally(() => setLoading(false)))();
  }, []);

  return countries;
};

export default myCustomHook;

The hook works great but I am using it in three different areas of my application despite the fact that all the countries are the same wherever the hook is used.

Is there a good pattern to call the axios request just once instead of three times?

EDIT - Final Code After Solution

import { useEffect, useState } from 'react';

import axios from 'axios';

let fakeCache = {
    alreadyCalled: false,
    countries: []
};

const myCustomHook = (forceUpdate = false) => {
  const [isLoading, setLoading] = useState(true);

  if (!fakeCache.alreadyCalled || forceUpdate) {
      fakeCache.alreadyCalled = true;

      (async () =>
        await axios
          .get("MY_API/countries")
          .then(response => setCountries(response.data))
          .finally(() => setLoading(false)))();
  }

  return countries;
};

export default myCustomHook;
like image 785
FabianoLothor Avatar asked Oct 27 '25 03:10

FabianoLothor


1 Answers

One solution to this would be to introduce a custom "cacheing layer" (between your hook and the axios request) that:

  1. caches countries data returned from the first successful request and,
  2. return the same cached data on subsequent requests

There are a number of ways this could be implemented - one possibility would be to define a getCountries() function that implements that cacheing logic in a separate module, and then call that function from your hook:

countries.js

import axios from 'axios';

// Module scoped variable that holds cache data
let cachedData = undefined;

// Example function wraps network request with cacheing layer
export const getCountries = async() => {

  // We expect the data for countries to be an array. If cachedData
  // is not an array, attempts to populate the cache with data
  if (!Array.isArray(cachedData)) {  
    const response = await axios.get("MY_API/countries");

    // Populate the cache with data returned from request
    cachedData = response.data;
  }

  return cachedData;
}

myCustomHook.js

import { useEffect, useState } from 'react';
import { getCountries } from "/countries";

const myCustomHook = () => {
  const [countries, setCountries] = useState([]);
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {

    (async() => {
      try {       
        setLoading(true);

        // Update hook state with countries data (cached or fresh)
        setCountries(await getCountries());

      } finally {
        setLoading(false)
      }
    }, []);
  });
}
export default myCustomHook;
like image 161
Dacre Denny Avatar answered Oct 28 '25 17:10

Dacre Denny



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!