1

I have an App component under which I have got three more component Login,Register,DashBoard and under the DashBoard component I have NavBar which has children as Content

Here is App.js

const App = () => {
  return (
    <div className="app">
      <Router>
        <Switch>
          <Route exact path="/" element={<Login />} />
          <Route exact path="/register" element={<Register />} />
          <Route exact path="/dashboard" element={<Dashboard />} />
        </Switch>
      </Router>
    </div>
  );
};
In Login.js component when a user login is successful then I'm storing the email and displayName into a user object in localStorage of the browser

Here is DashBoard.js

const Dashboard = () => {
  const { displayName, email } = JSON.parse(localStorage.getItem("user"));

  return (
    <NavBar displayName={displayName}>
      <Content></Content>
    </NavBar>
  );
};

Register.js

const Register = () => {
  const [register, setRegister] = useState({
    name: "",
    phoneNumber: "",
    email: "",
    password: "",
  });
  const navigate = useNavigate();
  const { name, phoneNumber, email, password } = register;
  const [user] = useAuthState(auth);

  const handleChange = (name) => (e) => {
    e.preventDefault();
    setRegister({ ...register, [name]: e.target.value });
  };

  useEffect(() => {
    if (loading) return;
    console.log(user);
    if (user) navigate("/dashboard", { replace: true });
  }, [user]);
  return (
    <div style={{ marginTop: "15rem" }}>
      <Container>
        <Row className="justify-content-lg-center">
          <Col xs>
            <FloatingLabel
              controlId="floatingInput"
              label="Name"
              className="mb-3"
            >
              <Form.Control
                type="text"
                placeholder="Doe joe"
                value={name}
                onChange={handleChange("name")}
              />
            </FloatingLabel>
            <FloatingLabel
              controlId="floatingPassword"
              label="Phone Number"
              className="mb-3"
            >
              <Form.Control
                type="number"
                placeholder="phoneNumber"
                value={phoneNumber}
                onChange={handleChange("phoneNumber")}
                name="email"
              />
            </FloatingLabel>
            <FloatingLabel
              controlId="floatingInput"
              label="Email Address"
              className="mb-3"
            >
              <Form.Control
                type="email"
                placeholder="Enter your Email.."
                value={email}
                onChange={handleChange("email")}
                name="email"
              />
            </FloatingLabel>
            <FloatingLabel
              controlId="floatingPassword"
              label="Password"
              className="mb-3"
            >
              <Form.Control
                type="password"
                placeholder="Password"
                value={password}
                onChange={handleChange("password")}
                name="phoneNumber"
              />
            </FloatingLabel>

            <Button
              variant="primary"
              onClick={() => {
                registerWithEmailAndPassword(
                  name,
                  phoneNumber,
                  email,
                  password
                );
                navigate("/");
              }}
            >
              Register
            </Button>
          </Col>
        </Row>
      </Container>
    </div>
  );
};

Here Firebase.js

const firebaseConfig = {
//firebase config
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

 //signIn Using google  

const logInWithEmailAndPassword = async (email, password) => {
  try {
    const res = await signInWithEmailAndPassword(auth, email, password);
    const user = res.user;
  } catch (err) {
    console.log(err);
    return err;
  }
};
const registerWithEmailAndPassword = async (
  name,
  phoneNumber,
  email,
  password
) => {
  try {
    const res = await createUserWithEmailAndPassword(
      auth,
      email,
      password,
      name,
      phoneNumber
    );
    const user = res.user;

    await updateProfile(auth.currentUser, {
      displayName: name,
    });
    await addDoc(collection(db, "users"), {
      uid: user.uid,
      name: name,
      phoneNumber: phoneNumber,
      authProvider: "local",
      email,
      password,
    });
  } catch (err) {
    console.error(err);
  }
};

   //logout
//exporting 

NavBar.js

const NavBar = (props) => {
  const [user] = useAuthState(auth); //react-firebase-hook/auth

  const navigate = useNavigate();
  const handleLogout = () => {
    logout();
  };
  useEffect(() => {
    if (!user) navigate("/");
  }, [user]);

  return (
    <>
      <Navbar bg="light" variant="light">
        <Container>
          <Navbar.Brand href="/dashboard">React-Bootstrap</Navbar.Brand>
          <Navbar.Toggle aria-controls="responsive-navbar-nav" />
          <Navbar.Collapse id="responsive-navbar-nav">
            <Nav className="me-auto"></Nav>
            <Nav>
              <Button variant="danger" onClick={handleLogout}>
                Logout
              </Button>
              <Nav.Link href="#deets"></Nav.Link>
              {user && (
                <Navbar.Text>Signed in as: {props.displayName}</Navbar.Text>
              )}
            </Nav>
          </Navbar.Collapse>
        </Container>
      </Navbar>
      {props.children}
    </>
  );
};

I'm displaying the name of the user in the Navbar component but after registering when the user is navigated to the root route which is login component which has an effect attached to it if the user then navigates to dashboard where the Navbar component will show the displayName but it is coming as null when I reload the page then it is showing the name. How can I show the displayName on initial render after registering when user is redirected to DashBoard

  • There is way too much code here to efficiently help. While somebody of course might happen to spot the problem, chances of that drastically increase of you provide a minimal repro. I recommend reproducing the problem from scratch in a minimal amount of code, as shown in [how to create a minimal, complete, verifiable example](http://stackoverflow.com/help/mcve) – Frank van Puffelen Oct 06 '22 at 13:21
  • @FrankvanPuffelen should I upload this on GitHub and share the repository here ? – Ayush Srivastava Oct 06 '22 at 13:36
  • Not really sure there is a way to reproduce this problem in a minimal way – Ayush Srivastava Oct 06 '22 at 13:42
  • @FrankvanPuffelen I had removed most of the code to make it minimal I think. – Ayush Srivastava Oct 06 '22 at 13:55

2 Answers2

0

You haven't included the NavBar component which you having issues with. It seems like a race condition in which the props are passed to NavBar before the values are set / retrieved. Perhaps try something like this in your NavBar component:

import { useEffect, useState } from 'react'
const NavBar = () => {

  const [displayName, setDisplayName] = useState('')
  const [email, setEmail] = useState('')

  // get the values when the component is rendered 
  useEffect(() => {
    const { displayName, email } = JSON.parse(localStorage.getItem("user"));
    setDisplayName(displayName)
    setEmail(email)
  }, [])

  return (
    <NavBar>
      <span>Email: {email}</span>
      <span>Display Name: {displayName}</span>
    </NavBar>
  );
};
Neil Girardi
  • 4,533
  • 1
  • 28
  • 45
  • I had to remove the navbar component as I thought It was not necessary but I'm adding it again and I implemented your solution but that didn't work – Ayush Srivastava Oct 06 '22 at 14:41
  • Neil I have added the NavBar component where I'm having issues – Ayush Srivastava Oct 06 '22 at 14:47
  • @AyushSrivastava it would appear that the items you are attempting to retrieve from local storage are not present in local storage at the time that the component renders. Perhaps post the code for the component that stores the data in localStorage? – Neil Girardi Oct 06 '22 at 17:01
  • useEffect(() => { if (user) { const userData = { displayName: user.displayName, email: user.email, }; localStorage.setItem("user", JSON.stringify(userData)); navigate("/dashboard"); } }, [user]); doing it from the login component that is Login.js – Ayush Srivastava Oct 06 '22 at 17:30
  • @AyushSrivastava I'm not clear on why it's not working. It sounds like you might be running into an issue similar to this: https://stackoverflow.com/questions/20231163/is-html5-localstorage-asynchronous whereby browser storage is supposed to be sync but is behaving as if it is async. Are you using Redux by any chance? If so, I would recommend to put the data there instead of localStorage. Otherwise, you can try adding a setTimeout of 0. – Neil Girardi Oct 06 '22 at 20:50
  • Yes that’s exactly my problem before storing it in local storage I was just passing displayName as a prop but that didn’t so I though of doing it this way as local storage would be in sync with the user but that too didn’t work . And currently I’m not using any of the state management library I thought it wouldn’t be necessary its really a small application just was testing the firebase authentication with react – Ayush Srivastava Oct 07 '22 at 08:42
  • 1
    You could use context API to store the email and displayName inside a provider that wraps your component tree and then access that context in your NavBar component. That would solve the problem of having to prop drill to pass the values into NavBar. – Neil Girardi Oct 07 '22 at 14:52
  • Yes that's definitely a solution but I didn't think to use state management tool for such a small application but anyways I don't have a choice I think – Ayush Srivastava Oct 08 '22 at 04:12
0

I kind of got a Solution to this, from the top of the component tree at App.js Added these lines

  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [user] = useAuthState(auth);

  useEffect(() => {
    setTimeout(() => {
      if (user !== null) {
        setName(user.displayName);
        setEmail(user.email);
      }
    }, 1000);
  }, [user]);

And then went on drilling down the props to the required component. Not a dependable solution but it is working for me right now