在 React 组件/redux 工具包之外的函数中使用 Dispatch
useDispatch in a function outside react component / redux tool kit
我需要帮助来解决这个错误:
"useDispatch is called in function that is neither a React function component nor a custom React Hook function".
解释:
store.js 和 userSlice.js 持有我的 Redux 相关事物(rtk)的定义。
Auth.js 旨在将功能保存到 authenticate/logout 并保持 redux“用户”存储更新。到目前为止,我只有 google 身份验证,当我调用 redirectToGoogleSSO 时,它已通过身份验证。
身份验证部分工作正常,我正在正确检索用户信息,但我很难让它更新用户存储。
dispatch(fetchAuthUser()) 是我收到错误的地方。
Sidebar.js 是一个导航侧边栏,它将包含一个菜单,用于注销 in/sign 并访问 profile.js(尚未实现) ).
如果我将 Auth 中的所有代码带到我的 Sidebar 组件中,身份验证工作和 redux 存储将被填充,但我想将内容保留在 Auth.js 中,以便我可以在其他组件中使用它,而不仅仅是在边栏。
//store.js:
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
export default configureStore({
reducer: {
user: userReducer
}
});
//userSlice.js
import { createSlice } from '@reduxjs/toolkit';
import axios from "axios";
export const userSlice = createSlice({
name: 'user',
initialState: {
email: 'teste@123',
name: 'teste name',
picture: 'teste pic',
isAuthenticated: false
},
reducers: {
setUser (state, actions) {
return {...state,
email: actions.payload.email,
name: actions.payload.name,
picture: actions.payload.picture,
isAuthenticated: true
}
},
removeUser (state) {
return {...state, email: '', name: '', picture: '', isAuthenticated: false}
}
}
});
export function fetchAuthUser() {
return async dispatch => {
const response = await axios.get("/api/auth/user", {withCredentials: true}).catch((err) => {
console.log("Not properly authenticated");
dispatch(removeUser());
});
if (response && response.data) {
console.log("User: ", response.data);
dispatch(setUser(response.data));
}
}
};
export const { setUser, removeUser } = userSlice.actions;
export const selectUser = state => state.user;
export default userSlice.reducer;
//Auth.js
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { fetchAuthUser } from '../../redux/userSlice';
export const AuthSuccess = () => {
useEffect(() => {
setTimeout(() => {
window.close();
},1000);
});
return <div>Thanks for loggin in!</div>
}
export const AuthFailure = () => {
useEffect(() => {
setTimeout(() => {
window.close();
},1000);
});
return <div>Failed to log in. Try again later.</div>
}
export const redirectToGoogleSSO = async() => {
const dispatch = useDispatch();
let timer = null;
const googleAuthURL = "http://localhost:5000/api/auth/google";
const newWindow = window.open(
googleAuthURL,
"_blank",
"toolbar=yes,scrollbars=yes,resizable=yes,top=200,left=500,width=400,height=600"
);
if (newWindow) {
timer = setInterval(() => {
if(newWindow.closed) {
console.log("You're authenticated");
dispatch(fetchAuthUser()); //<----- ERROR HERE ---->
if (timer) clearInterval(timer);
}
}, 500);
}
}
//Sidebar.js
import React from 'react';
import { Link } from 'react-router-dom';
import { redirectToGoogleSSO } from '../auth/Auth';
import { useSelector } from 'react-redux';
export const Sidebar = () => {
const handleSignIn = async() => {
redirectToGoogleSSO();
};
const {name,picture, isAuthenticated} = useSelector(state => state.user);
return (
<div id="sidenav" className="sidenav">
<div className="nav-menu">
<ul>
{
isAuthenticated
? <li>
<img className="avatar" alt="" src={picture} height="40" width="40"></img>
<Link to="/" className="user">{name}</Link>
<ul>
<li><Link to="/"><i className="pw-icon-export"/> logout</Link></li>
</ul>
</li>
: <li>
<Link to="/" className="login" onClick={handleSignIn}>
<i className="pw-icon-gplus"/>
Sign In / Sign Up
</Link>
</li>
}
</ul>
</div>
</div>
)
}
正如错误所述,您正在 Auth.js-> redirectToGoogleSSO
中调用 useDispatch
。这既不是 React Component 也不是 React Hook 函数。您需要在其中任何一个中调用 useDispatch
。所以你可以:
通过在handleSignIn
中同时调用useDispatch
和redirectToGoogleSSO
来处理组件中用户信息的redux部分和Google SSO部分本身(这现在可能更容易实现,你只需要将调度代码从 redirectToGoogleSSO
移动到 handleSignIn
),或者
将 redirectToGoogleSSO
变成一个可以从组件内部调用的 Hook。
您只能使用来自反应组件或自定义挂钩的 useDispatch 挂钩,在您的情况下,您应该使用 store.dispatch()
,尝试执行以下操作:
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
// following the docs, they assign configureStore to a const
const store = configureStore({
reducer: {
user: userReducer
}
});
export default store;
编辑:我还注意到你正在尝试调度一个不是动作的函数,redux 不能那样工作,你应该只调度你认为的动作在你的reducer中定义好了,否则你的state会不一致。
所以首先,将 fetchAuthUser
移动到另一个文件,例如 apiCalls.ts 或其他任何文件,这只是为了避免从 store.js
.
循环导入
之后,在 fetchAuthUser
上调用 store.dispatch:
// File with the fetch function
// Don't forget to change the path
import store from 'path/to/store.js'
export function fetchAuthUser() {
const response = await axios.get("/api/auth/user", {withCredentials: true}).catch((err) => {
console.log("Not properly authenticated");
store.dispatch(removeUser());
});
if (response && response.data) {
console.log("User: ", response.data);
store.dispatch(setUser(response.data));
}
};
在 Auth.js 中你不必调用调度,因为你已经在你的函数中调用了它。
export const redirectToGoogleSSO = async() => {
let timer = null;
const googleAuthURL = "http://localhost:5000/api/auth/google";
const newWindow = window.open(
googleAuthURL,
"_blank",
"toolbar=yes,scrollbars=yes,resizable=yes,top=200,left=500,width=400,height=600"
);
if (newWindow) {
timer = setInterval(() => {
if(newWindow.closed) {
console.log("You're authenticated");
// Just call the fetchAuthUser, you are already dispatching the state inside this function
await fetchAuthUser();
if (timer) clearInterval(timer);
}
}, 500);
}
}
所以请记住,如果你需要在 React 组件或自定义钩子之外使用分派,你 必须 使用 store.dispatch,否则它将无法工作,并且不要忘记仅调度操作以保持状态一致。我建议您阅读 core concepts about redux, and also see this video 以更好地了解它的工作原理。希望我有所帮助!
我需要帮助来解决这个错误:
"useDispatch is called in function that is neither a React function component nor a custom React Hook function".
解释:
store.js 和 userSlice.js 持有我的 Redux 相关事物(rtk)的定义。
Auth.js 旨在将功能保存到 authenticate/logout 并保持 redux“用户”存储更新。到目前为止,我只有 google 身份验证,当我调用 redirectToGoogleSSO 时,它已通过身份验证。
身份验证部分工作正常,我正在正确检索用户信息,但我很难让它更新用户存储。 dispatch(fetchAuthUser()) 是我收到错误的地方。
Sidebar.js 是一个导航侧边栏,它将包含一个菜单,用于注销 in/sign 并访问 profile.js(尚未实现) ). 如果我将 Auth 中的所有代码带到我的 Sidebar 组件中,身份验证工作和 redux 存储将被填充,但我想将内容保留在 Auth.js 中,以便我可以在其他组件中使用它,而不仅仅是在边栏。
//store.js:
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
export default configureStore({
reducer: {
user: userReducer
}
});
//userSlice.js
import { createSlice } from '@reduxjs/toolkit';
import axios from "axios";
export const userSlice = createSlice({
name: 'user',
initialState: {
email: 'teste@123',
name: 'teste name',
picture: 'teste pic',
isAuthenticated: false
},
reducers: {
setUser (state, actions) {
return {...state,
email: actions.payload.email,
name: actions.payload.name,
picture: actions.payload.picture,
isAuthenticated: true
}
},
removeUser (state) {
return {...state, email: '', name: '', picture: '', isAuthenticated: false}
}
}
});
export function fetchAuthUser() {
return async dispatch => {
const response = await axios.get("/api/auth/user", {withCredentials: true}).catch((err) => {
console.log("Not properly authenticated");
dispatch(removeUser());
});
if (response && response.data) {
console.log("User: ", response.data);
dispatch(setUser(response.data));
}
}
};
export const { setUser, removeUser } = userSlice.actions;
export const selectUser = state => state.user;
export default userSlice.reducer;
//Auth.js
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { fetchAuthUser } from '../../redux/userSlice';
export const AuthSuccess = () => {
useEffect(() => {
setTimeout(() => {
window.close();
},1000);
});
return <div>Thanks for loggin in!</div>
}
export const AuthFailure = () => {
useEffect(() => {
setTimeout(() => {
window.close();
},1000);
});
return <div>Failed to log in. Try again later.</div>
}
export const redirectToGoogleSSO = async() => {
const dispatch = useDispatch();
let timer = null;
const googleAuthURL = "http://localhost:5000/api/auth/google";
const newWindow = window.open(
googleAuthURL,
"_blank",
"toolbar=yes,scrollbars=yes,resizable=yes,top=200,left=500,width=400,height=600"
);
if (newWindow) {
timer = setInterval(() => {
if(newWindow.closed) {
console.log("You're authenticated");
dispatch(fetchAuthUser()); //<----- ERROR HERE ---->
if (timer) clearInterval(timer);
}
}, 500);
}
}
//Sidebar.js
import React from 'react';
import { Link } from 'react-router-dom';
import { redirectToGoogleSSO } from '../auth/Auth';
import { useSelector } from 'react-redux';
export const Sidebar = () => {
const handleSignIn = async() => {
redirectToGoogleSSO();
};
const {name,picture, isAuthenticated} = useSelector(state => state.user);
return (
<div id="sidenav" className="sidenav">
<div className="nav-menu">
<ul>
{
isAuthenticated
? <li>
<img className="avatar" alt="" src={picture} height="40" width="40"></img>
<Link to="/" className="user">{name}</Link>
<ul>
<li><Link to="/"><i className="pw-icon-export"/> logout</Link></li>
</ul>
</li>
: <li>
<Link to="/" className="login" onClick={handleSignIn}>
<i className="pw-icon-gplus"/>
Sign In / Sign Up
</Link>
</li>
}
</ul>
</div>
</div>
)
}
正如错误所述,您正在 Auth.js-> redirectToGoogleSSO
中调用 useDispatch
。这既不是 React Component 也不是 React Hook 函数。您需要在其中任何一个中调用 useDispatch
。所以你可以:
通过在
handleSignIn
中同时调用useDispatch
和redirectToGoogleSSO
来处理组件中用户信息的redux部分和Google SSO部分本身(这现在可能更容易实现,你只需要将调度代码从redirectToGoogleSSO
移动到handleSignIn
),或者将
redirectToGoogleSSO
变成一个可以从组件内部调用的 Hook。
您只能使用来自反应组件或自定义挂钩的 useDispatch 挂钩,在您的情况下,您应该使用 store.dispatch()
,尝试执行以下操作:
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';
// following the docs, they assign configureStore to a const
const store = configureStore({
reducer: {
user: userReducer
}
});
export default store;
编辑:我还注意到你正在尝试调度一个不是动作的函数,redux 不能那样工作,你应该只调度你认为的动作在你的reducer中定义好了,否则你的state会不一致。
所以首先,将 fetchAuthUser
移动到另一个文件,例如 apiCalls.ts 或其他任何文件,这只是为了避免从 store.js
.
之后,在 fetchAuthUser
上调用 store.dispatch:
// File with the fetch function
// Don't forget to change the path
import store from 'path/to/store.js'
export function fetchAuthUser() {
const response = await axios.get("/api/auth/user", {withCredentials: true}).catch((err) => {
console.log("Not properly authenticated");
store.dispatch(removeUser());
});
if (response && response.data) {
console.log("User: ", response.data);
store.dispatch(setUser(response.data));
}
};
在 Auth.js 中你不必调用调度,因为你已经在你的函数中调用了它。
export const redirectToGoogleSSO = async() => {
let timer = null;
const googleAuthURL = "http://localhost:5000/api/auth/google";
const newWindow = window.open(
googleAuthURL,
"_blank",
"toolbar=yes,scrollbars=yes,resizable=yes,top=200,left=500,width=400,height=600"
);
if (newWindow) {
timer = setInterval(() => {
if(newWindow.closed) {
console.log("You're authenticated");
// Just call the fetchAuthUser, you are already dispatching the state inside this function
await fetchAuthUser();
if (timer) clearInterval(timer);
}
}, 500);
}
}
所以请记住,如果你需要在 React 组件或自定义钩子之外使用分派,你 必须 使用 store.dispatch,否则它将无法工作,并且不要忘记仅调度操作以保持状态一致。我建议您阅读 core concepts about redux, and also see this video 以更好地了解它的工作原理。希望我有所帮助!