store/index.js
import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "../reducers/index";
const store = configureStore({
reducer: {
user: rootReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
}),
});
export default store;
userSlice.js
import { createSlice } from "@reduxjs/toolkit";
export const INITIAL_STATE = {
name: "Hello World",
email: "",
photo: "",
};
export const userSlice = createSlice({
name: "user",
initialState: INITIAL_STATE,
reducers: {
setUserLoginDetails: (state, action) => {
console.log("Initial", state.name);
state.name = action.payload.name;
state.email = action.payload.email;
state.photo = action.payload.photo;
console.log("Updated", state.name);
},
setSignOutState: (state) => {
state.name = null;
state.email = null;
state.photo = null;
},
},
});
export const { setUserLoginDetails, setSignOutState } = userSlice.actions;
export const selectUserName = (state) => state.user.name;
export const selectUserEmail = (state) => state.user.email;
export const selectUserPhoto = (state) => state.user.photo;
export default userSlice.reducer;
AuthContext.js
import React, { useContext, createContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
GoogleAuthProvider,
signInWithPopup,
signInWithRedirect,
signOut,
onAuthStateChanged,
} from "firebase/auth";
import { auth } from "../firebase";
import { setUserLoginDetails, selectUserName } from "../reducers/userSlice";
import store from "../store/index";
const AuthContext = createContext();
export const AuthContextProvider = ({ children }) => {
const dispatch = useDispatch();
const username = useSelector(selectUserName);
const googleSignIn = () => {
const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
.then((result) => {
console.log("Check Initial Store", store.getState());
console.log("Check Initial Name", username);
setUser(result.user);
console.log("Check Updated Store", store.getState());
console.log("Check Updated Name", username);
})
.catch((e) => {
const eCode = e.code;
const eMessage = e.message;
const eEmail = e.email;
const eCredential = e.credential;
});
};
const setUser = (user) => {
dispatch(
setUserLoginDetails({
name: user.displayName,
email: user.email,
photo: user.photoURL,
})
);
};
return (
<AuthContext.Provider value={{ googleSignIn, username }}>
{children}
</AuthContext.Provider>
);
};
export default AuthContext;
export const UserAuth = () => {
return useContext(AuthContext);
};
App.js
import {
BrowserRouter as Router,
Route,
Routes,
Navigate,
} from "react-router-dom";
import "./App.css";
import Header from "./components/Header";
import Home from "./components/Home";
import Login from "./components/Login";
import { AuthContextProvider, UserAuth } from "./context/AuthContext";
import { useEffect } from "react";
//import { useSelector } from "react-redux";
//import { selectUserName } from "./reducers/userSlice";
import { onAuthStateChanged } from "firebase/auth";
function App(props) {
const { username } = UserAuth();
useEffect(() => {
//if (username != null) {
console.log("User name is", username);
//}
}, [username]);
return (
<div className="App">
<AuthContextProvider>
<Router>
<Routes>
<Route
exact
path="/"
element={/*username ? <Navigate to="/home" /> : */<Login />}
/>
<Route
path="/home"
element={
/*!username ? (
<Navigate to="/" />
) :*/ (
<>
<Header />
<Home />
</>
)
}
/>
{/*<Route path="/redirect" element={<Navigate to="/home" />} />*/}
</Routes>
</Router>
</AuthContextProvider>
</div>
);
}
export default App;
The problem lies in AuthContext.js
, where I am attempting to select the current user name via useSelector(SelectUserName)
from userSlice.js, which is (state) => state.user.name
. The console.log
outputted the log in the attached image:
Edit: I forgot to add my reducers/index.js
so here you go.
reducers/index.js
import { combineReducers } from "redux";
import userReducer from "./userSlice";
const rootReducer = combineReducers({
userState: userReducer,
});
export default rootReducer;
Thanks a lot everyone for the response. I've tried converting this under userSlice.js
:
from
selectUserName = (state) => state.user.name;
to
selectUserName = (state) => state.user.userState.name;
And I was able to get the username
proper, rather than unknown
. Any further suggestion will be greatly appreciated.
Based on the screenshot you've included in your post it is clear that the Redux store has the following structure:
{
user: {
userState: {
email: "....",
name: "....",
photo: "....",
},
},
}
In other words, to select the name
state it is not state.user.name
, but rather state.user.userState.name
.
You can fix the selector functions to select the appropriate state using the correct path.
Example:
export const selectUserName = (state) => state.user.userState.name;
export const selectUserEmail = (state) => state.user.userState.email;
export const selectUserPhoto = (state) => state.user.userState.photo;
It might just be more likely the case that you inadvertently "nested" this userSlice.reducer
when exporting/combining into the rootReducer
. It looks like you merged/combined a userState
reducer when creating the rootReducer
and then merged the root reducer as user
when configuring the store.
The root reducer and store configuration should look similar to the following based on your current selector functions:
../userSlice.js selectors
export const selectUserName = (state) => state.user.name;
export const selectUserEmail = (state) => state.user.email;
export const selectUserPhoto = (state) => state.user.photo;
export default userSlice.reducer;
../reducers/index.js
import { combineReducers } from 'redux';
import userReducer from '../userSlice';
const rootReducer = combineReducers({
user: userReducer,
});
export default rootReducer;
store/index.js
import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "../reducers";
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
}),
});
export default store;