Hello guys so i tried to make global state so the other page can use the state. The problem is i got an error that says:
Login.js:18 Uncaught TypeError: Cannot destructure property 'emailLog' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined.
Im doing this because i want the email from user after logged in and pass them to another page so that i can display the logged in user.
App.js:
export const EmailUser = React.createContext();
function App() {
Axios.defaults.withCredentials = true;
const [invoice, setInvoice] = useState("");
const [currency, setCurrency] = useState("IDR");
const [myFile, setMyFile] = useState("");
const [emailLog, setEmailLog] = useState("");
return (
<EmailUser.Provider value={{ emailLog, setEmailLog }}>
<div className="App">
<BasicExample />
<div className="formInput">
<form method="POST" encType="multipart/form-data" action="http://localhost:3001/upload">
<div className="textUser"></div>
<input
className="inputForm"
defaultValue={emailLog}
type="email"
disabled
name="email"
/>
<input className="inputForm" type="number" placeholder="Invoice No" name="InvoiceNo" />
<input className="inputForm" type="date" name="Invoice_Date" />
<input className="inputForm" type="text" placeholder="Description" name="Description" />
<select
className="selectBox"
name="Currency"
onChange={(e) => {
setCurrency(e.target.value);
}}
>
<option value="IDR">IDR</option>
<option value="USD">USD</option>
<option value="YEN">YEN</option>
</select>
<input className="inputForm" type="number" placeholder="Amount" name="Amount" />
<input
className="custom-file-upload"
multiple
type="file"
name="DocumentFile"
onChange={(e) => {
setMyFile(e.target.value);
}}
/>
<button className="btnSubmit">Submit</button>
</form>
</div>
</div>
</EmailUser.Provider>
);
}
export default App;
Login.js
const Login = () => {
let navigate = useNavigate();
const { emailLog, setEmailLog } = useContext(EmailUser);
const [passwordLog, setPasswordLog] = useState("");
const [loginStatus, setLoginStatus] = useState("");
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault();
Axios.post("http://localhost:3001/login", {
email: emailLog,
password: passwordLog,
}).then((response) => {
console.log(response);
if (response.data.message) {
alert(response.data.message);
} else {
setLoginStatus(response.data[0].email);
alert("Redirecting");
navigate("/home");
}
});
};
console.log(emailLog);
useEffect(() => {
Axios.get("http://localhost:3001/login").then((response) => {
if (response.data.loggedIn == true) {
setLoginStatus(response.data.email[0].email);
}
});
});
return (
<div>
<img className="wave" src={Wave} />
<img className="wave2" src={WaveV2} />
<div className="wrapper">
<div className="img">{/* <img src={Background}/> */}</div>
<div className="register-content">
<div className="registerForm">
<img src={Avatar} />
<h2 className="title">Welcome</h2>
<div className="input-div one">
<div className="i">
<i className="fas fa-user">
<GrMail />
</i>
</div>
<div className="div">
<input
type="email"
className="input"
placeholder="Email"
required
onChange={(e) => {
setEmailLog(e.target.value);
}}
/>
</div>
</div>
<div className="input-div pass">
<div className="i">
<i className="fas fa-lock">
<AiFillLock />
</i>
</div>
<div className="div">
<input
type="password"
className="input"
placeholder="Password"
required
onChange={(e) => {
setPasswordLog(e.target.value);
}}
/>
</div>
</div>
<a href="/">Don't have an account ?</a>
<button type="submit" className="btn" onClick={login}>
Login
</button>
</div>
</div>
</div>
</div>
);
};
export default Login;
Advertisement
Answer
EmailUser context works only with the components that are children of EmailUser.Provider, and it doesn’t seem to be the case for Login component. An easy way is to create a separate component, in some EmailUserProvider.js, like so:
import {createContext, useState} from "react"
export const EmailUser = createContext();
export default function EmailUserProvider({children}) {
const [emailLog, setEmailLog] = useState("");
return (
<EmailUser.Provider value={{ emailLog, setEmailLog }}>
{children}
</EmailUser.Provider>
);
}
And make it wrap all the components that would consume it. If I assume all my components and routes are rendered in App and want the context to be global, I would do so:
<EmailUserProvider> <App/> </EmailUserProvider>