7

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>
    );
};
Jakub Roztocil
  • 15,930
  • 5
  • 50
  • 52
Tony Caterev
  • 127
  • 1
  • 2
  • 13

2 Answers2

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.

James
  • 80,725
  • 18
  • 167
  • 237
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>}
);
François Richard
  • 6,817
  • 10
  • 43
  • 78
  • 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. – Tony Caterev Feb 13 '20 at 14:26
  • 1
    Why `user.length`? Doesn't look like `user` would be an array here – James Feb 13 '20 at 14:26
  • const [user, setUser] = useState([]); ? – François Richard Feb 13 '20 at 15:10
  • 1
    @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. – James Feb 13 '20 at 16:23
  • yeah true about the loop – François Richard Feb 13 '20 at 16:58