asynchronous context with useEffect in React
Asked Answered
C

2

8

im trying to create an api request with the header value, that is received from a context component. However, as soon as the page component is loaded, it throws an Cannot read property '_id' of null exception. Is there a way to run the useEffect function, as soon as the context is loaded?

main component:

import React, { useState, useEffect, useContext } from "react";
import "./overview.scss";

/* COMPONENTS */;
import axios from 'axios';
import { GlobalContext } from '../../components/context/global';

const Overview = () => {
  const [bookings, setBookings] = useState([]);
  const [loaded, setLoaded] = useState(false);

  const [user, setUser] = useContext(GlobalContext);

  useEffect(() => {
      axios
      .get(`/api/v1/bookings/user/${user._id}`)
      .then(res => setBookings(res.data))
      .catch(err => console.log(err))
      .finally(() => setLoaded(true));
  }, [user]);

context component:

import React, {useState, useEffect, createContext} from 'react';
import jwt from 'jsonwebtoken';

/* GLOBAL VARIABLES (CLIENT) */
export const GlobalContext = createContext();

export const GlobalProvider = props => {

    /* ENVIRONMENT API URL */
    const [user, setUser] = useState([]);

    useEffect(() => {
        const getSession = async () => {
            const user = await sessionStorage.getItem('authorization');
            setUser(jwt.decode(user));
    }
    getSession();
    }, [])

    return (
        <GlobalContext.Provider value={[user, setUser]}>
            {props.children}
        </GlobalContext.Provider>
    );
};
Conny answered 13/2, 2020 at 14:3 Comment(0)
F
13

The issue here is useEffect is running on mount, and you don't have a user yet. You just need to protect against this scenario

useEffect(() => {
  if (!user) return;

  // use user._id
},[user])

Naturally, when the Context fetches the user it should force a re-render of your component, and naturally useEffect should re-run as the dependency has changed.

Forspent answered 13/2, 2020 at 14:33 Comment(0)
U
0

put a condition before rendering you GlobalProvider, for example:

 return (
    {user.length&&<GlobalContext.Provider value={[user, setUser]}>
        {props.children}
    </GlobalContext.Provider>}
);

If user is not an array just use this

 return (
    {user&&<GlobalContext.Provider value={[user, setUser]}>
        {props.children}
    </GlobalContext.Provider>}
);
Ury answered 13/2, 2020 at 14:12 Comment(5)
Thanks for your reply, but unfortunately it throws me an Cannot read property length of null. I suppose that the async function has been not loaded, yet.Conny
Why user.length? Doesn't look like user would be an array hereForspent
const [user, setUser] = useState([]); ?Fetus
@FrançoisRichard ah I see, the default value for the state is misleading as it doesn't get used as an array (nor would the use of language suggest it, all singular). Also, FWIW, you wouldn't want to make user a dependency of useEffect in the context otherwise you would end up with a continuous loop of fetching user data.Forspent
yeah true about the loopFetus

© 2022 - 2024 — McMap. All rights reserved.