英文:
@reduxjs/toolkit - UseSelector returning Undefined instead of Google Username
问题
你在AuthContext.js
中尝试使用useSelector(SelectUserName)
来选择当前用户的名称,这是从userSlice.js
中的(state) => state.user.name
选择的。但你在控制台中看到了"unknown"。
你已经尝试了将selectUserName
从selectUserName = (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:
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;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论