Custom hook to await callback from useEffect hook

I have a web application, iwth an instance of a keycloack.js, where i tried to use a custom hook, to fetch a userprofile, before the UI renders. The reason for that, is that the actual application requires, some information in localStorage before the ui should render.

Here is the logic i have tried to implement in my App.js component

import React, {Suspense} from 'react';
import 'bootstrap/dist/css/bootstrap.css';
import 'appkit-react/style/appkit-react.default.css';
import PageHub from './Components/Navigation/PageHub';
import Spinner from './Components/Shared/Spinner';
import LoadUserProfile from './_helpers/hooks/loadUserProfile'
import './i18n';

function App(props) {


  const [loading, error] = LoadUserProfile(props.keycloak)
  console.log(props)
  if(loading){
    return <Spinner />
  }
  if(error){
    console.log(error)
    return <div>Error occured!!</div>
  }
  console.log(loading)
  console.log(error)
  return (
      <Suspense fallback={<Spinner/>}>
        <PageHub/>
      </Suspense>
  );
}

export default App;

and the LoadUserProfile hook looks like this.

import React, { useState, useEffect } from 'react';
import Spinner from '../../Components/Shared/Spinner';

function LoadUserProfile(keycloak){
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState({});

    useEffect(() =>{
        setLoading(true)
        keycloak.loadUserProfile().success((profile) => {
            console.debug('Loaded profile ' + profile);
            localStorage.setItem("firstName", keycloak.profile.firstName);
            localStorage.setItem("lastName", keycloak.profile.lastName);
            localStorage.setItem('GUID', keycloak.profile.attributes.guid[0])
            setLoading(false)
        }).error(() => {
            console.error('Failed to load profile')
            setLoading(false)
            setError({"error": "No user found"})
        }, [keycloak, loading, error]);

    })


    return [loading, error]

}
export default LoadUserProfile;

right now it goes into an infinite loop.

Upvotes: 0

Views: 548

Answers (1)

Chitova263
Chitova263

Reputation: 789

So first of all you must rename your custom hook to useLoadUserProfile as recommended in the React Documentation so that React would be able to automatically check for violations of rules of Hooks. The infinite loop is caused by the inclusion of the loading and error states in your dependency array. Whenever you make a call to 'setMyState' in this case setLoading or setError

  1. React reruns your custom hook again and returns updated loading or error states
  2. The useEffect hook is then run again and calls setLoading or setError which causes React to run your function again.... hence the infinite loop.

So to fix this you must remove the loading and error states from your dependency array. The Rule of thumb is:
if you specify the dependency array, all values from inside your component that are used by the effect must be there. Including props, state, functions — anything in your component. i.e never lie about your dependencies.

In your case your useEffect hook does not even depend on your loading or error states. So they shouldn't be in the dependency array in the first place!

Upvotes: 2

Related Questions