How to wait for the render to complete to get useContext when using useEffect?

Viewed 1447

I have a conditional redirect from UserPage as shown below to an external authentication service. The condition is in such a way that if my UserContext does not contain the user object then the page will be redirected to https://urltoauthenticate:5000/user for user authentication. The condition is satisfied in the first run and goes to the authentication service

But the issue is happening after successful authentication. In the case of successful authentication even though the UserContext provides the user object, it will be null in the first render. Here how can I wait till the user object is available and ready so that the page will not be redirected to the authentication service again?

import React, { useEffect, useContext } from 'react';
import { UserContext } from '../context/UserContext';
export default function UserPage () {
  const user = useContext(UserContext)
  useEffect(() => {
    if(user === null{
      window.location.assign("https://urltoauthenticate:5000/user");
    }
  }, [users]);
  return(
    <div>Hello {user.name}</div>
  )
}
3 Answers

Instead of waiting for your context to be updated, make a redirect to the user's page when it actually gets updated. To make it you can put on your login page a component like this:

export const RedirectIfLoggedIn: React.FC = () => {
  const user = React.useContext(UserContext);
  return user ? <Redirect to={userPath} /> : null;
};

Additionally, pass that redirect guard to your routing:

export const PrivateRoute: React.FC<{
  component: React.FC;
  path: string;
  exact: boolean;
}> = (props) => {
   const user = useContext(UserContext);

  return user ? (
    <Route path={props.path} exact={props.exact} component={props.component} />
  ) : (
    <Redirect to={loginPath} />
  );
};
export const Routes: React.FC = () => {
  return ( 
    <Switch>
      <Route path={loginPath} component={LoginPage} />
      <PrivateRoute path={userPath} component={UserPage} />
      ...
    </Switch>
  );
};

I suspect there are couple of issues in your code.

  • users as dependency is passed while you have user. Note plural and singular use.
  • user should never be null. This returns context object. So, unless you destructed const {user} this will not be null.
  • Unless you pass the null value, it won't be null. null !== undefined

So your hook makes no sense to me. It never runs. Your resurrection is happening from somewhere else.

Now, to the point how to wait for the value?

No, you don't need to wait for the value. The context value must be consumed after you have defined it on the provider.

I guess you have not defined the default value while creating the context.

const {user} = useContext(UserContext)

The user is undefined. There's nothing wrong here. You're just consuming value before you provide in the provider.

What should be done?

When you login successfully, provide the value as user in the provider and then you can consume the value. Also, you don't need to use the effect hook. You will be able to get the value just so. I mean you can return redirect just after the user value const definition.

Make sure the provider is somewhere as parent component of UserPage component.

Just simply add useState

    import React, { useEffect, useContext, useState } from 'react';
    import { UserContext } from '../context/UserContext';
    export default function UserPage () {
      const [render, setRender] = useState(false)
      const user = useContext(UserContext)
      
      useEffect(() => {
        setRender(true)
      }, []);

      useEffect(() => {
        if(render === true) {
           if(user === null{
           window.location.assign("https://urltoauthenticate:5000/user");
           }
        }
      }, [render, user]);
      return(
        <div>Hello {user.name}</div>
      )
    }
Related