Reputation: 618
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
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
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