最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ReactReact Context API: Wait for context values when using useEffect() hook - Stack Overflow

programmeradmin4浏览0评论

I am developing a web app using React and here is what I am trying to do:

  1. In the highest-order component, check whether the user is logged in and if logged in, update 'userinfo' Context value.
  2. In Profile.js, get the 'userinfo' Context value.
  3. Check whether 'userinfo' is null or not, and call different APIs accordingly.

I wrote the below code. The problem is that there is apparently a time lag for the userinfo context value to be delivered to the component. So when using useEffect() hook, Profile.js will render twice when userinfo is not null.

Is there a way to fix this code so that it waits for 'userinfo' variable, and then call relevant APIs accordingly, not before?

Below is the code. Any advice? Thanks a lot in advance!

import React, {useEffect, useContext} from 'react';
import Userinfo from "../context/Userinfo";


function Profile(props) {

   const {userinfo, setuserinfo}=useContext(Userinfo);

   useEffect(()=>{      
       ((userinfo==null)?
       /*API call A (When user is not logged in) */ :
       /*API call B (When user is logged in) */
       )},[userinfo])  

   return (
       (userinfo==null)?
       /*Elements to render when user is not logged in) */ :
       /*Elements to render when user is  logged in) */
   );
}

export default Profile;

I am developing a web app using React and here is what I am trying to do:

  1. In the highest-order component, check whether the user is logged in and if logged in, update 'userinfo' Context value.
  2. In Profile.js, get the 'userinfo' Context value.
  3. Check whether 'userinfo' is null or not, and call different APIs accordingly.

I wrote the below code. The problem is that there is apparently a time lag for the userinfo context value to be delivered to the component. So when using useEffect() hook, Profile.js will render twice when userinfo is not null.

Is there a way to fix this code so that it waits for 'userinfo' variable, and then call relevant APIs accordingly, not before?

Below is the code. Any advice? Thanks a lot in advance!

import React, {useEffect, useContext} from 'react';
import Userinfo from "../context/Userinfo";


function Profile(props) {

   const {userinfo, setuserinfo}=useContext(Userinfo);

   useEffect(()=>{      
       ((userinfo==null)?
       /*API call A (When user is not logged in) */ :
       /*API call B (When user is logged in) */
       )},[userinfo])  

   return (
       (userinfo==null)?
       /*Elements to render when user is not logged in) */ :
       /*Elements to render when user is  logged in) */
   );
}

export default Profile;
Share Improve this question asked Apr 28, 2020 at 11:26 J.KoJ.Ko 8,7213 gold badges17 silver badges35 bronze badges
Add a comment  | 

4 Answers 4

Reset to default 13

The best solution here is to add a loading state in the context provider which is reset once the value is updated.

function Profile(props) {

   const {userinfo, loading, setuserinfo}=useContext(Userinfo);

   useEffect(()=>{      
       if(!loading) {
            ((userinfo==null)?
             /*API call A (When user is not logged in) */ :
            /*API call B (When user is logged in) */
            )
       }
    )},[loading, userinfo])  

   if(loading) return <Loader />
   return (
       (userinfo==null)?
       /*Elements to render when user is not logged in) */ :
       /*Elements to render when user is  logged in) */
   );
}

Lets assume you are setting up a context like below:

import React from 'react';
const AuthContext = React.createContext({
    userInfo: null,
    isLoggedIn: false,
    loading: true,
    setUser: () => {} 
});

export default AuthContext;

And you are setting up your Global Provider like this:

import React from 'react';
import  AuthContext  from './AuthContext'; // path to the context

export default class GlobalUserContext extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            userInfo: null,
            isLoggedIn: false,
            loading: true
            }
    }

    componentDidMount(){
        ( async() => {
            const currentUser = await getUser(); // say you are trying to log user in
            if(currentUser){
                this.setState({
                    userinfo: currentUser,
                    isLoggedIn: true,
                    loading: false
                });  
            }

        } )();
    }
    
    setUser = (user, check) => {
        this.setState({userInfo: user, isLoggedIn: check});
    };
    
    render(){
        return (
            <AuthContext.Provider 
            value={{
                user: this.state.user,
                setUser: this.setUser,
                isLoggedIn: this.state.isLoggedIn,
                loading: this.state.loading
            }}>
            {this.props.children} 
            </AuthContext.Provider>
        );
    }

}

Then in your functional component using useEffect,you can do something like this:

import React, { useContext, useEffect} from 'react';
import AuthContext                     from '../../contexts/AuthContext'; // your content path

export const MyComponent = () => {
const {userInfo, isLoggedIn, loading} = useContext(AuthContext);
useEffect(() => {
// here you can now ask if the user data is still loading like so:
if(!loading){
// do something 
// UserInfo is available
}
}, [userInfo, loading]);
return (
<View>
{anything}
</View>
);
}

I have this problem today, I like what Shubham does. But the way I solve it is just getting the userinfo from the previous component and pass it to this profile component.

However, make sure you render the Profile component like this so that you can make sure the userinfo exists:

function ParentComponent(){
 // do sth to get the userinfo here
return(
  <ParentComponent>
     {userinfo? <Profile userinfo={userinfo} /> : null}       
 <ParentComponent/>
)
}

I'm using Typescript and I was able to get around this issue by allowing my context object's typing to be either that object type itself, undefined, or null. I was trying to conditionally render a child component but I wanted to make sure it didn't even attempt to render that component, and run any of it's useEffect() code, until the asynchronous context code had run (default value of context object set to undefined), at which point the context object would equal either the object in storage or be manually set to null (if storage call failed).

Not sure if this is good practice or not, but it was a simple way of using undefined to represent the context's pre-loaded state of the object, and thus, only conditionally render some things if the context object was specifically null or not.

发布评论

评论列表(0)

  1. 暂无评论