gpt4 book ai didi

reactjs - useEffect 每次都会执行,即使依赖关系没有改变

转载 作者:行者123 更新时间:2023-12-02 16:11:02 26 4
gpt4 key购买 nike

我们有 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 }

每当我们在我们的 React 应用程序中导航到不同的页面时,我们都会看到每次都调用“用户”路由,即使 token 没有更新。我们的 token 仅在用户注销时更改。

我们的 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>
)
}

这是我们的内部应用程序,因此我们希望用户登录 30 天,他们不必每次都保持登录状态。因此,当用户第一次登录时,我们为他们创建一个 token 并将该 token 保存在 cookie 中。因此,如果用户关闭浏览器并再次回来,我们会检查 cookie 中的 token 。如果 token 存在,我们调用 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;

我们的 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 }

我们的 getCookies 函数使用 universal-cookies 包简单地读取 cookie

    export const getCookies = (name) => {
return cookies.get(name);
};

最佳答案

所以我尝试使用 CodeSandbox 重现您的问题,这些是我基于您的代码的发现:

上下文:

您的上下文有一个 useEffect,它依赖于 token。当您调用 refreshToken 时,您更新了 token,它会自动触发 useEffect 并调用 fetchUserInfo。所以你不需要在 refreshToken 中的 setToken 之后调用 fetchUserInfo。你的上下文看起来像:


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 };

路线:

现在开始你的路由,因为你没有包含 AppRouter 的代码 我不得不做出一个假设你使用 react-router 带有 Switch 组件。 (如 CodeSandbox 所示)。

我在您的 Login 组件中看到一行是 window.location.replace(redirectTo);。当您这样做时,整个页面都会刷新(重新加载?)并且 React 会触发重新渲染,这就是为什么我认为您的上下文方法会再次触发。

而是使用 react-router 中的 history API (同样,我的假设) 就像这样,

let history = useHistory();
history.push(redirectTo);

如果你想玩的话,这里是沙盒:

Edit so-questions-68005144

关于reactjs - useEffect 每次都会执行,即使依赖关系没有改变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68005144/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com