Home > other >  React useEffect renders twice
React useEffect renders twice

Time:11-26

I know that React will render twice when using a hook like this one:

const [userPermissions, setUserPermissions] = useState();

//Use GET from myService to save to state
useEffect(() => {
  myService.canUserAccess(userId)
  .then(({userPermissions}) => setUserPermissions(userPermissions));
});

This is a huge problem given my application's logic because i need to check for the user permissions and redirect the user if the permissions are not right:

useEffect(() => {
  console.log(userPermissions); //This is the output
  if(!userPermissions){ redirect(...)}
},[userPermissions, redirect]);

I have been debugging this and it seems that there is a first 'render': that outputs this:

undefined

And right after:

{ userPermissions: {...} }

Following my application's logic, when the state of userPermissions is first set to undefined, it will be redirected. I need to fetch this userPermissions object but the 'double render' of React is preventing me to execute the logic as desired.

Is there a way to 'load' the userPermissions object and setting it to the useState hook without triggering the 'double render' ?

CodePudding user response:

Can you store another piece of state to know if it's loaded or not?

const [gettingUserPermissions, setGettingUserPermissions] = useState(true)

useEffect(() => {
  if(gettingUserPermissions){
    return; // wait until fully loaded
  }
  console.log(userPermissions); //This is the output
  if(!userPermissions){ redirect(...)}
},[gettingUserPermissions, userPermissions, redirect]);

Not sure how you're calling the initial loading of getting user permissions but I'm sure this can be adapted.

CodePudding user response:

You can implement an extra state to check whether the promise of the asynchronous function (myService) has been resolved or not. This will skip the unwanted undefined results of userPermissions state.

const [userPermissions, setUserPermissions] = useState();
const [resolved, isResolved] = useState(false);

useEffect(() => {
  (async () => {
    const userPermissions = await myService.canUserAccess(userId);
    setUserPermissions({ userPermissions });
    isResolved(true);
  })();
}, [myService]);

useEffect(() => {
  if (resolved) {
    if (!userPermissions) {
      redirect(...)
    }
  }
}, [resolved, userPermissions, redirect]);

CodePudding user response:

To me it seems most reasonable to just merge it into one useEffect, but this may not make sense in full context of your code.

const [userPermissions, setUserPermissions] = useState();

//Use GET from myService to save to state
useEffect(() => {
  myService.canUserAccess(userId)
  .then(({userPermissions}) => {
    if(!userPermissions){ redirect(...)}
    setUserPermissions(userPermissions)
  });
});
  • Related