我们有 UserContext哪些设置我们可以在整个应用程序中使用的用户对象。UserContext即使依赖没有改变,我们每次都继续执行并且不必要地进行 api 调用。
import React, { useState, useEffect } from 'react'
import APIService from './utils/APIService';
import { getCookies } from './utils/Helper';
const UserContext = React.createContext();
const UserContextProvider = (props) => {
const [token, setToken] = useState(getCookies('UserToken'));
const [user, setUser] = useState(null);
useEffect(() => {
console.log('Inside userContext calling as token ', token)
fetchUserInfo();
}, [token]);
const fetchUserInfo = async() => {
if (token) {
let userRes = await APIService.get(`/user?token=${token}`);
console.log('User route called')
setUser(userRes.data);
}
}
/*
If user logoff or login, update token from child component
*/
const refreshToken = (newToken) => {
//token = newToken;
setToken(newToken);
fetchUserInfo()
}
return (
<UserContext.Provider value={{user, token, refreshToken}}>
{props.children}
</UserContext.Provider>
);
}
export { UserContextProvider, UserContext }
Run Code Online (Sandbox Code Playgroud)
每当我们导航到 React 应用程序中的不同页面时,即使令牌未更新,我们也会看到每次都调用“用户”路由。我们的令牌仅在用户注销时更改。
我们的 AppRouter 如下所示;
import React from 'react';
import AppRouter from "./AppRouter";
import { Container } from 'react-bootstrap';
import Header from './components/Header';
import { ToastProvider, DefaultToastContainer } from 'react-toast-notifications';
import 'bootstrap/dist/css/bootstrap.css';
import './scss/styles.scss';
import { UserContextProvider } from './UserContextProvider';
export default function App() {
const ToastContainer = (props) => (
<DefaultToastContainer
className="toast-container"
style={{ zIndex:100,top:50 }}
{...props}
/>
);
return (
<UserContextProvider>
<ToastProvider autoDismiss={true} autoDismissTimeout={3000} components={{ ToastContainer }}>
<Container fluid>
<Header />
<AppRouter />
</Container>
</ToastProvider>
</UserContextProvider>
)
}
Run Code Online (Sandbox Code Playgroud)
这是我们的内部应用程序,因此我们希望用户可以登录 30 天,而不必每次都保持登录状态。因此,当用户第一次登录时,我们为他们创建一个令牌并将该令牌保存在 cookie 中。因此,如果用户关闭浏览器并再次返回,我们会在 cookie 中检查令牌。如果令牌存在,我们会调用 API 来获取用户信息并在我们的上下文中设置用户。这是不工作的部分,它在导航到应用程序中的每条路线期间不断调用我们的用户 api。
这是我们的 login.js
import React, { useState, useContext } from 'react';
import { setCookies } from '../../utils/Helper';
import APIService from '../../utils/RestApiService';
import { UserContext } from '../../UserContextProvider';
import queryString from 'query-string';
import './_login.scss';
const Login = (props) => {
const [email, setEmail] = useState(null);
const [password, setPassword] = useState(null);
const [error, setError] = useState(null);
const { siteId } = props;
const { refreshToken} = useContext(UserContext);
const onKeyPress = (e) => {
if (e.which === 13) {
attemptLogin()
}
}
let params = queryString.parse(props.location.search)
let redirectTo = "/"
if (params && params.redirect)
redirectTo = params.redirect
const attemptLogin = async () => {
const payload = {
email: email,
password: password,
siteid: siteId
};
let response = await APIService.post('/login', payload);
console.log('response - ', response)
if (response.status === 200) {
const { data } = response;
setCookies('UserToken', data.token);
refreshToken(data.token)
window.location.replace(redirectTo);
}
else {
const { error } = response.data;
setError(error);
}
}
const renderErrors = () => {
return (
<div className="text-center login-error">
{error}
</div>
)
}
return (
<div className="login-parent">
<div className="container">
<div className="login-row row justify-content-center align-items-center">
<div className="login-column">
<div className="login-box">
<form className="login-form form">
<h3 className="login-form-header text-center">Login</h3>
<div className="form-group">
<label>Email:</label>
<br/>
<input
onChange={e => setEmail(e.target.value)}
placeholder="enter email address"
type="text"
onKeyPress={onKeyPress}
className="form-control"/>
</div>
<div className="form-group">
<label>Password:</label>
<br/>
<input
onChange={e => setPassword(e.target.value)}
placeholder="enter password"
type="password"
className="form-control"/>
</div>
<div className="form-group">
<button
className="btn btn-secondary btn-block"
onClick={attemptLogin}
type="button">
Login
</button>
</div>
{error ? renderErrors() : null}
</form>
</div>
</div>
</div>
</div>
</div>
)
}
export default Login;
Run Code Online (Sandbox Code Playgroud)
我们的 userContext 如下所示
import React, { useState, useEffect } from 'react'
import APIService from './utils/APIService';
import { getCookies } from './utils/Helper';
const UserContext = React.createContext();
const UserContextProvider = (props) => {
const [token, setToken] = useState(getCookies('UserToken'));
const [user, setUser] = useState(null);
useEffect(() => {
if (!token) return;
console.log('Inside userContext calling as token ', token)
fetchUserInfo();
}, [token]);
const fetchUserInfo = async() => {
if (token) {
let userRes = await APIService.get(`/user?token=${token}`);
console.log('User route called')
setUser(userRes.data);
}
}
/*
If user logoff or login, update token from child component
*/
const refreshToken = (newToken) => {
//token = newToken;
setToken(newToken);
fetchUserInfo()
}
return (
<UserContext.Provider value={{user, token, refreshToken}}>
{props.children}
</UserContext.Provider>
);
}
export { UserContextProvider, UserContext }
Run Code Online (Sandbox Code Playgroud)
我们的 getCookies 函数使用 Universal-cookies 包简单地读取 cookie
export const getCookies = (name) => {
return cookies.get(name);
};
Run Code Online (Sandbox Code Playgroud)
所以我尝试使用 CodeSandbox 复制您的问题,这些是我根据您的代码发现的结果:
您的上下文有一个useEffect取决于token. 当您调用 时refreshToken,您会更新token自动触发useEffect并调用 的fetchUserInfo。所以你不需要fetchUserInfo在setTokenin之后调用refreshToken。您的上下文如下所示:
const UserContext = React.createContext();
const UserContextProvider = (props) => {
const [token, setToken] = useState(getCookies("UserToken"));
const [user, setUser] = useState(null);
useEffect(() => {
console.log("Inside userContext calling as token ", token);
fetchUserInfo();
}, [token]);
const fetchUserInfo = async () => {
if (token) {
let userRes = await APIService.get(`/user?token=${token}`);
console.log('User route called')
setUser(userRes.data);
}
};
const refreshToken = (newToken) => {
setToken(newToken);
};
return (
<UserContext.Provider value={{ user, token, refreshToken }}>
{props.children}
</UserContext.Provider>
);
};
export { UserContextProvider, UserContext };
Run Code Online (Sandbox Code Playgroud)
现在来到你的路由,因为你已经不包含代码的AppRouter ,我不得不作出一个假设您使用react-router与Switch组件。(如 CodeSandbox 所示)。
我看到您的Login组件中有一行是window.location.replace(redirectTo);. 当你这样做时,整个页面会刷新(重新加载?)并且 React 会触发重新渲染,这就是为什么我认为你的上下文方法再次触发。
而是像这样使用(再次,我的假设)中的historyAPI ,react-router
let history = useHistory();
history.push(redirectTo);
Run Code Online (Sandbox Code Playgroud)
如果你想玩,这里是沙箱:
| 归档时间: |
|
| 查看次数: |
151 次 |
| 最近记录: |