Oyyou
Oyyou

Reputation: 618

React Context Provider - useContext undefined error in hook

I'm currently in the process of creating a context provider to keep track of a logged in user

When the user logs in, I want to call a hook that uses the "setUser" function to update the "user" in state

I've created a cut-down version of my app in CodePen here

Breakdown:

Step 1

Create the 'UserContext' with a default value

const UserContext = React.createContext( {
  user: { name:"Jeoff", id: -1 }, 
  setUser: () => null
});

Step 2

Create the provider wrapper where I pass in the "user", and "setUser"

const UserContextProvider = ({ children }) => {

  const [user, setUser] = React.useState();

  return (
    <UserContext.Provider
      value={{
        user,
        setUser
      }}
    >
      {children}
    </UserContext.Provider>
  )
}

Step 3

Create the hook. The hook returns a higher-order function that is called later, and deconstructs the 'UserContext'.

function useLoginUser() {
  const { setUser } = React.useContext(UserContext); // This causes the error

  async function loginUser(user) {
    setUser(user);
  }

  return loginUser;
}

Step 4

Implement the 'loginUser' function, and call it.

const App = () => {

  const loginUser = useLoginUser;

  const onSubmit = () => {
    try {
      loginUser({name:"Text", id: 1});
    } catch(e){
      console.log(e);
    }
  }

  return (
    <UserContextProvider>
      <div className="container">
        <h1>Please help me (:</h1>
        <button onClick={onSubmit}>Login</button>
      </div>
    </UserContextProvider>
  )
}

As mentioned, the error comes in Step 3 when we try and deconstruct the 'UserContext'. The error in CodePen wasn't being much use, and it wasn't in my local environment either, to be honest. The error was coming out as 'undefined'

After quite a lot of Googling, it appears people tend to have this issue when they're using their 'Context' outside the scope of the 'Provider', but I don't believe that's the issue here.

Upvotes: 2

Views: 3257

Answers (2)

sanjarcode
sanjarcode

Reputation: 570

Solution: use the Provider on a higher level, like in index.js or the currently accepted solution.


You're trying to access the context outside the Provider (tree). This is not allowed since it doesn't exist up top there.

They are in the same file, yes, but it's still outside the tree.

Also, using a custom hook doesn't really change anything, since you're using it outside the tree, too.

In Redux, an error is raised for this situation. Unfortunately, the Context API stays silent, which can lead to pesky bugs. It's still nice you got an error, BTW.

Upvotes: 0

Darshan Sachaniya
Darshan Sachaniya

Reputation: 515

The problem with your code is the consumer. You did not wrap the Consumer properly with the Provider. Here is your solution:

Step 1 - Create a context:

const UserContext = React.createContext( {
  user: { name:"Jeoff", id: -1 },
  setUser: () => {}
});

Step 2 - Create a context provider:

const UserContextProvider = ({ children }) => {

  const [user, setUser] = React.useState({ name:"Jeoff", id: -1 });

  return (
      <UserContext.Provider
          value={{
            user,
            setUser
          }}
      >
        {children}
      </UserContext.Provider>
  )
};

Step 3 - Create a context consumer:

const ReactContextConsumer = () => {

  const {  user, setUser  } = React.useContext(UserContext);

  const onSubmit = () => {
    try {
      setUser({name:"Text", id: 1});
    } catch(e){
      console.log(e);
    }
  };

  return (
      <div className="container">
        <h1>Please help me (:</h1>
        <button onClick={onSubmit}>Login</button>
        {user.name + user.id}
      </div>
  )
};

Step 5 - Use the consumer inside the context provider:

const App = () => {
  return (
      <UserContextProvider>
        <ReactContextConsumer/>
      </UserContextProvider>
  )
};

Upvotes: 1

Related Questions