Search code examples
reactjsweb-component

React Wont Caching Results of API request in Local storage


I'm trying to build a very simple component in react that requests an image from an API and subsequently stores / caches it for any other request in the application when that component is used.

At the moment, the component ALWAYS renders, and as such - if I have the component multiple times on the page (i.e. in the first 10 results) I get multiple API calls, whereas I was expecting to only get 1 API call, with the result coming back and being stuck into localstorage.

Anyone any ideas? Should I be using useMemo, or something else? Any ideas on how to prevent the multiple API calls?

import React, {useState, useEffect} from 'react';
import UserService from '../../api/UserService';

const ProfileImage = () => {
   const userService  = new UserService();
   const [avatar, setAvatar] = useState();
   const cachedAvatar = localStorage.getItem('avatar');

   const mount = () => {
      console.log(cachedAvatar);

      if (cachedAvatar) {
         setAvatar(cachedAvatar);
       } else {
         userService.getUser().then((res)=>{
            setAvatar( res.avatarUrl );
            localStorage.setItem('avatar', res.avatarUrl);
         });
       }

   }

   /* unmount cleanup */
   useEffect(() => {
      mount();
   },[]);

   return (
      <img src={  avatar ? avatar : require('../../img/placeholder/avatar.png')}></img>
   );
};

export default ProfileImage;

Solution

  • You have multiple API calls because the avatar is fulfilled inside the localStorage only after the first call ends. Using localStorage is not the right solution.

    You need to not call the getUser every time. For example:

    const userService = new UserService();
    let getUserCall;
    // create a custom hook
    const useAvatar = () => {
       const [avatar, setAvatar] = useState();
       useEffect(() => {
          if (!getUserCall) {
            getUserCall = userService.getUser();
          }
          getUserCall.then(res => setAvatar(res.setAvatar))
       },[]);
       return avatar;
    }
    

    Then inside your component use the useAvatar hooks.

    const ProfileImage = () => {
        const avatar = useAvatar();
        return (
         <img src={  avatar ? avatar : defaultAvatar  }> 
         </img>
        );
    }