@reduxjs/toolkit – UseSelector 返回 undefined 而不是 Google 用户名

huangapple go评论79阅读模式
英文:

@reduxjs/toolkit - UseSelector returning Undefined instead of Google Username

问题

你在AuthContext.js中尝试使用useSelector(SelectUserName)来选择当前用户的名称,这是从userSlice.js中的(state) => state.user.name选择的。但你在控制台中看到了"unknown"。

你已经尝试了将selectUserNameselectUserName = (state) => state.user.name; 改成 selectUserName = (state) => state.user.userState.name;,这是正确的做法,因为你的根 reducer 是userState而不是user,所以应该选择state.user.userState.name来获得用户名。

如果你有其他问题或需要进一步的帮助,请随时提出。

英文:

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:

@reduxjs/toolkit – UseSelector 返回 undefined 而不是 Google 用户名

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.

答案1

得分: 0

这部分可能是您商店设置的问题:

reducer: {
user: rootReducer,
},

rootReducer 究竟是什么?这看起来您可能希望像这样做:

reducer: rootReducer,

或者像这样设置每个部分的 reducers:

reducer: {
user: userReducer,
},

更多信息请查看:https://redux-toolkit.js.org/tutorials/quick-start#add-slice-reducers-to-the-store

英文:

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

答案2

得分: 0

根据您提供的屏幕截图,可以清楚地看到Redux存储库具有以下结构:

{
  user: {
    userState: {
      email: "....",
      name: "....",
      photo: "....",
    },
  },
}

换句话说,要选择name状态,不是state.user.name,而是state.user.userState.name

您可以修复选择器函数,以使用正确的路径选择适当的状态。

示例:

export const selectUserName = (state) => state.user.userState.name;
export const selectUserEmail = (state) => state.user.userState.email;
export const selectUserPhoto = (state) => state.user.userState.photo;

很可能情况是您在导出/组合userSlice.reducer时不小心“嵌套”了它。看起来您在创建rootReducer时合并了userState reducer,然后在配置存储时将根reducer合并为user

根reducer和存储配置应根据您当前的选择器函数类似于以下内容:

../userSlice.js选择器

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;
英文:

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;

huangapple
  • 本文由 发表于 2023年4月11日 04:03:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/75980316.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定