Home > database >  Uncaught TypeError: Cannot destructure property when using React context
Uncaught TypeError: Cannot destructure property when using React context

Time:08-27

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;

CodePudding user response:

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>
  • Related