Home > Mobile >  Why Context variable is not available before component renders in React Native?
Why Context variable is not available before component renders in React Native?

Time:01-17

In a React Native app, I have a Login component that stores the userId in the app Context. After loging-in, the user goes to the Dashboard component where the userId is accessed by using the useContext hook. The userId is required to fetch posts from the database. I use the useEffect hook to call the fetching function when the component mounts.
The probelm is that when the useEffect executes and the function is called, the userId is undefined. However, if I re render the component then I can see, by the logs, that the id is now defined but because the useEffect has executed I do not get the data from the server. I have tried to solve the issue using the setTimeOut() method in the useEffect hook but again the fetch takes place before the id is defined.

What is most strange to me is that, in the Dashboard component, I follow the same steps to access a Token from the context and this Token is immediately available.

Below, I show you my code and the logs of the app:

This is the Context file
//AuthContext.tsx
import { createContext, useState } from "react";
import { AuthUser } from "../types/AuthUser";


type AuthContextProviderProps = {
    children: React.ReactNode
}


type AuthContextType= {
    
    isAuthenticated : boolean
    setIsAuthenticated : React.Dispatch<React.SetStateAction<boolean>>
    token : string
    setToken : React.Dispatch<React.SetStateAction<string>>
    userId : string
    setUserId : React.Dispatch<React.SetStateAction<string>>
    userName : string
    setUserName : React.Dispatch<React.SetStateAction<string>>
    
}

export const AuthContext = createContext({} as AuthContextType)

export const AuthContextProvider = ({children}: AuthContextProviderProps)=>{
   
    const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false)
    const [token, setToken] = useState<string>('')
    const [userId, setUserId] = useState<string>('')
    const [userName, setUserName] = useState<string>('')
    //const [authUser, setAuthUser] = useState<AuthUser>({userId:'', userName:''})
    return(
        <AuthContext.Provider  value={{isAuthenticated, setIsAuthenticated,token, setToken, userId,setUserId,userName,setUserName}}>
        {children}
        </AuthContext.Provider>
    )
}

In the Login screen I store userId and token in the Context

const loginUser = async(): Promise<ISuccessFullResponse | boolean>=>{

    try {
        
        const body = {email, password}
        const url = REMOTE_SERVER '/authentication/loginUser'
        console.log(url)
        const response = await fetch(url,{
        method : 'POST',
        headers:{'content-type':'application/json'},
        body : JSON.stringify(body)

    })
    
    console.log(loggedUser)
    const id = loggedUser.data.user._id
    const name = loggedUser.data.user.userName
    setToken(loggedUser.data.token)
    setUserId(id)
    setUserName(name)
    return loggedUser
    } catch (error) {
        console.log(error)
        }
        
        return false
    }
    
}

After login, there is a redirection to the Dashboard. ''' const Dashboard : React.FC = (props)=>{

//interfaces
interface IPost{
    contentString : string
    urls : string[]
}
//global state
const authContext = useContext(AuthContext)
const token = authContext.token
console.log('token in dashboard', token)
const id = authContext.userId
console.log('id in dashboard', id)



const profilePicContext = useContext(ProfilePicContext)
const setProfilePic = profilePicContext.setProfilePic

//local state
const [arrayOfPosts, setArrayOfPosts] = useState<IPost[]>([])
const [status, setStatus] = useState<string>('')
const [errMsg, setErrMsg] = useState<string>("")

//props
const navigation = props.navigation

//function definitions
const getProfilePicture = async():Promise<boolean>=>{
   
    try {
        const response = await fetch(REMOTE_SERVER '/dashboard/downloadProfilePicture', {
            headers : {token}

        })
        const parseResponse = await response.json()
        if(parseResponse.data){
            setProfilePic(parseResponse.data)
        }
        
    } catch (error) {
        console.log(error)
        if(error instanceof Error){
            console.log('this is the error',error)
            return false
        }
    }
    return true 
}

const getPostsFromFollowingUsers = async():Promise<boolean>=>{
    setStatus('LOADING')
    try {
      
           
           console.log('inside function id', id)
           
            const response = await fetch(REMOTE_SERVER `/userPost/getPostsFromFollowingUsers/${id}`, {
                method : 'GET',
                headers : {'Content-Type': 'application/json'}
                })
            const parseResponse = await response.text()
            console.log('this is the parseresponse',parseResponse)
            const responseObject = JSON.parse(parseResponse)
       
            if(responseObject.data){
                setArrayOfPosts(responseObject.data)
            }      
    } catch (error) {
        console.log('this is the error',error)
        setStatus('ERROR')
        if(error instanceof Error){
           setErrMsg(error.message)
            
        }
        
    }    
    return true
}


 useEffect(()=>{
    getProfilePicture()
}, [])  
useEffect(()=>{
    getPostsFromFollowingUsers()
}, []) 
  

return(

''' As you can see, the constant token was stored and accessed in the same way as the userId, yet the token is accessible in the dashboard and is successfully used in getProfilePicture(). On the other hand, userId, at first is undefined and is becomes defined after getPostFromFollowingUsers() is called. Here are the logs:

token in dashboard eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjJkMTg1YjI2NTliZDEzMjlhNDVjMzBkIiwiaWF0IjoxNjczODAzMDQ5LCJleHAiOjE2NzM4MzkwNDl9.HdJn0YAfi-yM9wPZMek6zmw4SUE2TsR_AzUOK06MeBg
 LOG  id in dashboard
 LOG  name in dashboard
 LOG  inside function id
 LOG  token in dashboard eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjJkMTg1YjI2NTliZDEzMjlhNDVjMzBkIiwiaWF0IjoxNjczODAzMDQ5LCJleHAiOjE2NzM4MzkwNDl9.HdJn0YAfi-yM9wPZMek6zmw4SUE2TsR_AzUOK06MeBg
 LOG  id in dashboard 62d185b2659bd1329a45c30d
 LOG  name in dashboard
 LOG  token in dashboard eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjJkMTg1YjI2NTliZDEzMjlhNDVjMzBkIiwiaWF0IjoxNjczODAzMDQ5LCJleHAiOjE2NzM4MzkwNDl9.HdJn0YAfi-yM9wPZMek6zmw4SUE2TsR_AzUOK06MeBg
 LOG  id in dashboard 62d185b2659bd1329a45c30d
 LOG  name in dashboard Joaquin
 LOG  token in dashboard eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjJkMTg1YjI2NTliZDEzMjlhNDVjMzBkIiwiaWF0IjoxNjczODAzMDQ5LCJleHAiOjE2NzM4MzkwNDl9.HdJn0YAfi-yM9wPZMek6zmw4SUE2TsR_AzUOK06MeBg
 LOG  id in dashboard 62d185b2659bd1329a45c30d
 LOG  name in dashboard Joaquin
 LOG  this is the parseresponse <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /userPost/getPostsFromFollowingUsers/</pre>
</body>
</html>
 LOG  this is the error [SyntaxError: JSON Parse error: Unrecognized token '<']

Naturally, the route needed a parameter to work and that is why I get this error. Now id I type a space in Visual Studio Code useEffect is called again and now the userId is defined so the fetch is performed and the data from the server is retrieved. If someone shared an idea on how to solve this issue, I would be grateful.

CodePudding user response:

add a loading state to your component and set it true when you are fetching the data and then set it to false when the data is fetched, show an activity loader when it is loading and this might solve your issue.

const { userId } = useContext(AppContext);

useEffect(() => {
    if (userId) {
        fetchData(userId).then(data => {
            setIsLoading(false);
        });
    }
}, [userId]);
  • Related