Upon logging in via Google OAuth, users are redirected to the home page, and their user data is stored in localStorage. The objective is to immediately show the user's name and profile on the navbar without the need to refresh the page. Presently, this information becomes visible only after a page refresh. The desired functionality is to redirect users to the home page upon login and instantly display login details on the navbar.
auth.js
import React, { useState } from 'react'
import { Avatar, Button, Paper, Grid, Typography, Container, TextField } from '@mui/material'
import LockClockOutlinedIcon from '@mui/icons-material/LockClockOutlined';
import { GoogleLogin } from '@react-oauth/google';
import { useDispatch } from 'react-redux';
import { jwtDecode } from "jwt-decode";
import { AUTH } from '../constants/actionTypes';
import { setAuthData } from '../../reducers/auth';
import useStyles from './styles'
import Input from './Input';
import Icon from './icon'
import { useNavigate } from 'react-router-dom';
export default function Auth() {
const [isSignup,setIsSignUp] = useState(false)
const classes = useStyles()
const [showPassword, setShowPassword] = useState(false)
const dispatch = useDispatch()
const navigate = useNavigate()
const handleSubmit = () => {
}
const handleChange = () => {
}
const handleShowPassword = () => {
setShowPassword((prevpassowrd) => !prevpassowrd)
}
const switchMode = () => {
setIsSignUp((prevflag) => !prevflag)
}
const googleSucess = async (res) => {
// if profile obj exists it returns or else it throws an error
// const token = jwtDecode(res.credential).jti
// console.log("this is token", token)
try {
const cred = res.credential
dispatch(setAuthData(cred))
navigate('/')
}catch (error){
console.log("error dispatching", error)
}
}
const googleFailure = (error) => {
console.error('Sign-in failed:', error);
}
return (
<Container component='main' maxWidth="xs">
<Paper className={classes.paper} elevation={3}>
<Avatar className={classes.avatar}>
<LockClockOutlinedIcon />
</Avatar>
<Typography variant='h5'>{isSignup ? 'Sign Up' : 'Sign In'}</Typography>
<form className={classes.form} onSubmit={handleSubmit}>
<Grid container spacing={2}>
{
isSignup && (
<>
<Input name='firstName' label='First Name' handleChange={handleChange} autoFocus half />
<Input name='lastName' label='Last Name' handleChange={handleChange} half />
</>
)
}
<Input name='email' label='Email Address' handleChange={handleChange} type='email' />
<Input name='password' label='Password' handleChange={handleChange} type={showPassword ? 'text' : 'password'}
handleShowPassword={handleShowPassword} />
{isSignup &&
<Input name='confirmpassword' label='Confirm Password' handleChange={handleChange} type='password' />}
</Grid>
<Button type='submit' fullWidth variant='contained' color='primary' style={{ marginTop: '15px', marginBottom : '15px' }}>
{isSignup ? 'Sign Up' : 'Sign In'}
</Button>
<GoogleLogin
clientId='client id goes here'
render={(renderProps) => (
<Button
style={{ marginTop: '15px' }}
className={classes.googleButton}
color='primary'
fullWidth
onClick={renderProps.onClick}
disabled = {renderProps.disabled}
startIcon={<Icon />}
variant='contained'
>Google {isSignup ? 'Sign Up' : 'Sign In'}</Button>
) }
onSuccess={googleSucess}
onFailure={googleFailure}
cookiePolicy='single_host_origin'
plugin_name = 'dont work'
/>
<Grid container justify = 'center'>
<Grid item style={{ marginTop: '15px', textAlign: 'center' }}>
<Button onClick={switchMode} style={{ marginTop: '15px', textAlign: 'center' }}>
{isSignup ? 'Already Have an account? Sign In' : 'Don\'t have account? Sign Up'}
</Button>
</Grid>
</Grid>
</form>
</Paper>
</Container>
)
}
reducers_auth.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { jwtDecode } from "jwt-decode";
const initialState = {
authData: null,
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setAuthData(state, action) {
const user_data = jwtDecode(action.payload);
// Modify the draft state using Immer
state.authData = user_data;
// Update localStorage outside of Immer
localStorage.setItem('profile', JSON.stringify(user_data));
},
logoutAuth(state, action) {
localStorage.removeItem('profile')
}
}
})
export const { setAuthData, logoutAuth } = authSlice.actions;
export default authSlice.reducer;
navbar.js
import { AppBar, Typography, Toolbar, Avatar, Button, Box,Grid,Container } from "@mui/material";
import React, { useEffect, useState } from "react";
import { Link } from 'react-router-dom'
import useStyles from './styles'
import memories from '../../images/memories.png'
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import { logoutAuth } from "../../reducers/auth";
const Navbar = () => {
const classes = useStyles()
const dispatch = useDispatch()
const navigate = useNavigate()
const [user,setUser] = useState(JSON.parse(localStorage.getItem('profile')))
useEffect(() => {
if (user) {
setUser(JSON.parse(localStorage.getItem('profile')))
}
}, [])
const logout = () => {
dispatch(logoutAuth())
navigate('/auth')
setUser(null)
}
return (
// <Box sx={{ flexGrow: 1 }}>
<>
<AppBar className={classes.appBar} position='static' color='inherit'>
<Container maxWidth="xl" style={{ display: 'flex', flexDirection: 'row' }}>
<div className={classes.brandContainer}>
<img className={classes.image} src={memories} alt="memories" height="50" />
<Typography variant='h4' nowrap className={classes.heading} >Travel Blog</Typography>
</div>
<Toolbar className={classes.toolbar} style={{ marginLeft: 'auto' }}>
{user ? (
<div className={classes.profile}>
<Avatar className={classes.purple} alt={user.name} src={user.picture}>{user.name.charAt(0)}</Avatar>
<Typography className={classes.userName} variant='h6'>{user.name}</Typography>
<Button variant="contained" className={classes.logout} color="secondary" onClick={logout} >Logout</Button>
</div>
) : (
<Button component={Link} to="/auth" variant="contained" color="primary" style={{ marginLeft: 'auto' }}>
Sign In
</Button>
)}
</Toolbar>
</Container>
</AppBar>
</>
)
}
export default Navbar
The Navbar
component should read the user data that was stored in the Redux state, not what is stored in localStorage. Updates to localStorage don't trigger React to rerender.
Example:
...
import { useDispatch, useSelector } from "react-redux";
...
const Navbar = () => {
const classes = useStyles();
const dispatch = useDispatch();
const navigate = useNavigate();
const user = useSelector(state => state.auth.authData);
const logout = () => {
dispatch(logoutAuth());
navigate('/auth');
};
return (
...rendered UI with user data...
);
};
export default Navbar;
The logoutAuth
action should reset the auth state. Query the initial auth state from localStorage.
const initialState = {
authData: JSON.parse(localStorage.getItem("profile")),
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setAuthData(state, action) {
const user_data = jwtDecode(action.payload);
state.authData = user_data;
localStorage.setItem('profile', JSON.stringify(user_data));
},
logoutAuth(state, action) {
localStorage.removeItem('profile');
state.authData = null; // <-- reset to initial
},
},
});