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>