skip to Main Content

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);
    //}
  }, );

  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:

console.log

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.

2

Answers


  1. This part of your store setup may be the issue:

    reducer: {
        user: rootReducer,
      },
    

    What exactly is rootReducer? This looks like you either want to do something like this instead:

    reducer: rootReducer,
    

    Or set the reducers for each slice like so:

    reducer: {
        user: userReducer,
      },
    

    https://redux-toolkit.js.org/tutorials/quick-start#add-slice-reducers-to-the-store

    Login or Signup to reply.
  2. 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;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search