Skip to content
Advertisement

Uncaught TypeError: Cannot destructure property when using React context

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>
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement