I tried using useContext for the first time and don't know what I'm doing wrong, the bold lines are the ones with the (Uncaught TypeError: React.useContext(...) is undefined). I have imported the usercontext and checked if the file location is right too, even updated the dependencies. What I'm doing wrong?
UserContext:
import React from 'react';
import { TOKEN_POST, USER_GET } from 'Api';
export const UserContext = React.createContext();
export const UserStorage = ({ children }) => {
const [data, setData] = React.useState(null);
async function getUser(token) {
const { url, options } = USER_GET(token);
const response = await fetch(url, options);
const json = await response.json();
setData(json);
setLogin(true);
console.log(json);
}
async function userLogin(username, password) {
const { url, options } = TOKEN_POST({ username, password });
console.log(url);
console.log(options);
const tokenRes = await fetch(url, options);
const { token } = await tokenRes.json();
window.localStorage.setItem('token', token);
getUser(token);
}
return (
<UserContext.Provider value={{ userLogin, data }}>
{children}
</UserContext.Provider>
);
};
Header:
import React from 'react';
import styles from './Header.module.css';
import { UserContext } from '../UserContext';
const Header = () => {
const { data } = React.useContext(UserContext);
return (
<header className={styles.header}>
<nav className={`${styles.nav} container`}>
<Link className={styles.logo} to="/" aria-label="Dogs - Home">
<Dogs />
</Link>
{ data && data.email }
<Link className={styles.login} to="/login">
Login / Criar
</Link>
</nav>
</header>
);
};
export default Header;
LoginForm:
import React from 'react';
import { Link } from 'react-router-dom';
import Input from '../Forms/Input';
import Button from '../Forms/Button';
import useForm from '../../Hooks/useForm';
import { UserContext } from '../../UserContext';
const LoginForm = () => {
const username = useForm();
const password = useForm();
const { userLogin } = React.useContext(UserContext);
async function handleSubmit(event) {
event.preventDefault();
if (username.validate() && password.validate()) {
userLogin(username.value, password.value)
}
}
return (
<section>
<h1>Login</h1>
<form action="" onSubmit={handleSubmit}>
<Input label="Usuário" type="text" name="username" {...username} />
<Input label="Senha" type="password" name="password" {...password} />
<Button>Entrar</Button>
</form>
<Link to="/login/criar">Cadastro</Link>
</section>
);
};
export default LoginForm;
I think you are not wrapping children/pages which are using this context. Make sure you have wrapped UserStorageProvider
in your App.js
.
You can do something like:
/// Update your context file like this
import React, { useContext } from 'react';
import { TOKEN_POST, USER_GET } from 'Api';
const UserContext = React.createContext();
export const UserStorageProvider = ({ children }) => {
const [data, setData] = React.useState(null);
async function getUser(token) {
const { url, options } = USER_GET(token);
const response = await fetch(url, options);
const json = await response.json();
setData(json);
setLogin(true);
console.log(json);
}
async function userLogin(username, password) {
const { url, options } = TOKEN_POST({ username, password });
console.log(url);
console.log(options);
const tokenRes = await fetch(url, options);
const { token } = await tokenRes.json();
window.localStorage.setItem('token', token);
getUser(token);
}
return (
<UserContext.Provider value={{ userLogin, data }}>
{children}
</UserContext.Provider>
);
};
/// create this custom hook to read UserStorage context
export const useUserStorage = ()=> useContext(UserContext);
In your App.js
or layout (if you are using) do as follow:
import { UserStorageProvider } from 'your/path'
export default function App() {
return (
<UserStorageProvider>
<Header />
<LoginPage />
</UserStorageProvider>
)
}
And in your component you can use the custom hook as follow:
Header
import React from 'react';
import styles from './Header.module.css';
import { useUserStorage } from '../UserContext';
const Header = () => {
const { data } = useUserStorage()
....
}