I have a React Application which just contains Authtentication Functionality. I created a dashboard as a home page which displays users email, and a navbar which checks if user is logged in or not. If user logged in, it displays a dropdown with a title user's email(dropdown items: update profile/log out) else, it just displays a log in button.
In the Update Profile page, User can change password and email.
My problem is, if user chanhges email, navbar do not update itself but dashboard do.
App.js:
import "./style.css";
import Navigation from "./Navbar";
import Signup from "./Signup";
import { AuthProvider } from "../contexts/AuthContext";
import Dashboard from "./Dashboard";
import Login from "./Login";
import Logout from "./Logout";
import ForgotPassword from "./ForgotPassword";
import PrivateRoute from "./PrivateRoute";
import UpdateProfile from "./UpdateProfile";
function App() {
return (
<Router >
<AuthProvider>
<Navigation />
<Routes>
<Route
path="/"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
></Route>
<Route
path="/update-profile"
element={
<PrivateRoute>
<UpdateProfile />
</PrivateRoute>
}
></Route>
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />} />
<Route path="/logout" element={<Logout />} />
<Route path="/forgot-password" element={<ForgotPassword />} />
</Routes>
</AuthProvider>
</Router>
);
}
export default App;
Navbar.js:
import Container from "react-bootstrap/Container";
import Nav from "react-bootstrap/Nav";
import Navbar from "react-bootstrap/Navbar";
import NavDropdown from "react-bootstrap/NavDropdown";
import { useAuth } from "../contexts/AuthContext";
import { useEffect, useState } from "react";
const Navigation = () => {
const { currentUser } = useAuth();
const [email, setEmail] = useState("");
return (
<div>
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand>
<Nav.Link as={Link} to="/">
Commercial
</Nav.Link>
</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav>
<NavDropdown
title="Man"
className="dropdown"
id="basic-nav-dropdown"
renderMenuOnMount={true}
>
<NavDropdown.Item>Shirt</NavDropdown.Item>
<NavDropdown.Item>Jean</NavDropdown.Item>
</NavDropdown>
<NavDropdown
title="Woman"
id="basic-nav-dropdown"
className="dropdown"
renderMenuOnMount={true}
>
<NavDropdown.Item>Dress</NavDropdown.Item>
<NavDropdown.Item>Skirt</NavDropdown.Item>
</NavDropdown>
</Nav>
</Navbar.Collapse>
<Navbar.Collapse
className="justify-content-end"
id="responsive-navbar-nav"
>
<Nav>
{currentUser ? (
<NavDropdown
title={currentUser.email}
className="dropdown"
id="basic-nav-dropdown"
renderMenuOnMount={true}>
<Nav.Link as={Link} to="/update-profile">
Go Profile
</Nav.Link>
<Nav.Link as={Link} to="/logout">
Log Out
</Nav.Link>
</NavDropdown>
) : (
<Nav.Link as={Link} to="/login">
Login
</Nav.Link>
)}
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</div>
);
};
export default Navigation;
Dashboard.js:
import { Card, Button, Alert, Container } from "react-bootstrap";
import { useAuth } from "../contexts/AuthContext";
const Dashboard = () => {
const { currentUser } = useAuth();
return (
<Container className="mt-5 d-flex align-items-center justify-content-center">
<div className="w-100" style={{ maxWidth: "400px" }}>
<Card>
<Card.Body>
<h2 className="text-center mb-4">Profile</h2>
<strong>Email</strong> {currentUser.email}
</Card.Body>
</Card>
</div>
</Container>
);
};
export default Dashboard;
I think I should re-render the navbar every time the page is loaded. How can I do that, or is there another solutions? PS:I have not any functional error/bug. Everything works fine.
EDIT: I am sharing other codes, may be this can help:
AuthContext.js:
import React, { useContext, useState, useEffect } from "react";
import { auth } from "../firebase";
const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
const signup = (email, password) => {
return auth.createUserWithEmailAndPassword(email, password);
};
const login = (email, password) => {
return auth.signInWithEmailAndPassword(email, password)
}
const logout = () => {
return auth.signOut();
}
const resetPassword = (email) => {
return auth.sendPasswordResetEmail(email);
}
const updateEmail = (email) => {
return currentUser.updateEmail(email);
}
const updatePassword = (password) => {
return currentUser.updatePassword(password);
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
resetPassword,
updateEmail,
updatePassword
};
return (
<AuthContext.Provider value={value}>
<div></div>
{!loading && children}
</AuthContext.Provider>
);
}
PrivateRoute.js:
import { Navigate } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext";
const PrivateRoute = ({ children }) => {
const { currentUser } = useAuth();
return currentUser ? children : <Navigate to="/login" />;
};
export default PrivateRoute;
UpdateProfile.js:
import { Form, Button, Card, Container, Alert } from "react-bootstrap";
import { useAuth } from "../contexts/AuthContext";
import { Link, useNavigate } from "react-router-dom";
const UpdateProfile = () => {
const emailRef = useRef();
const passwordRef = useRef();
const passwordConfirmRef = useRef();
const { currentUser, updateEmail, updatePassword } = useAuth();
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const navigator = useNavigate();
async function handleSubmit(e) {
e.preventDefault();
if (passwordRef.current.value !== passwordConfirmRef.current.value) {
return setError("Passwords do not match");
}
const promises = [];
setLoading(true);
if(emailRef.current.value !== currentUser.email){
promises.push(updateEmail(emailRef.current.value));
}
if(passwordRef.current.value){
promises.push(updatePassword(passwordRef.current.value));
}
Promise.all(promises).then(() => {
navigator("/");
}).catch(() =>{
setError("Failed to update Account");
}).finally(() =>{
setLoading(false);
})
}
return (
<Container className="mt-5 d-flex align-items-center justify-content-center">
<div className="w-100" style={{ maxWidth: "400px" }}>
{error && <Alert variant="danger">{error}</Alert>}
<Card>
<Card.Body>
<h2 className="text-center mb-4">Update Profile</h2>
<Form onSubmit={handleSubmit}>
<Form.Group id="email">
<Form.Label>Email</Form.Label>
<Form.Control
type="email"
ref={emailRef}
defaultValue={currentUser.email}
required
/>
</Form.Group>
<Form.Group id="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
ref={passwordRef}
placeholder="Leave blank to keep the same"
/>
</Form.Group>
<Form.Group id="passwordConfirm">
<Form.Label>Password Confirmation</Form.Label>
<Form.Control
type="password"
ref={passwordConfirmRef}
placeholder="Leave blank to keep the same"
/>
</Form.Group>
<Button disabled={loading} type="submit" className="w-100 mt-3">
Update
</Button>
</Form>
</Card.Body>
</Card>
<div className="w-100 text-center mt-2">
<Link to="/" style={{ textDecoration: "none" }}>
Cancel
</Link>
</div>
</div>
</Container>
);
};
export default UpdateProfile;