我试图将身份验证添加到我的应用程序中,并使用React维护Auth。
我使用自定义钩子use-http调用我的api。
import { useCallback, useReducer } from 'react';
function httpReducer(state, action) {
switch (action.type) {
case 'SEND':
return {
data: null,
error: null,
status: 'pending',
};
case 'SUCCESS':
return {
data: action.responseData,
error: null,
status: 'completed',
};
case 'ERROR':
return {
data: null,
error: action.errorMessage,
status: 'completed',
};
default:
return state;
}
}
function useHttp(requestFunction, startWithPending = false) {
const [httpState, dispatch] = useReducer(httpReducer, {
status: startWithPending ? 'pending' : null,
data: null,
error: null,
});
const sendRequest = useCallback(
async requestData => {
dispatch({ type: 'SEND' });
try {
const responseData = await requestFunction(requestData);
dispatch({ type: 'SUCCESS', responseData });
} catch (error) {
dispatch({
type: 'ERROR',
errorMessage: error.response.data.message || 'Something went wrong!',
});
}
},
[requestFunction]
);
return {
sendRequest,
...httpState,
};
}
export default useHttp;这是我的登录页面,它调用api,我需要从这个页面导航,并更新我的Auth上下文。
import { useCallback, useContext } from 'react';
import { makeStyles } from '@material-ui/core';
import Container from '@material-ui/core/Container';
import LoginForm from '../components/login/LoginForm';
import useHttp from '../hooks/use-http';
import { login } from '../api/api';
import AuthContext from '../store/auth-context';
import { useEffect } from 'react';
const useStyles = makeStyles(theme => ({
pageWrapper: {
height: '100vh',
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.palette.background.default,
},
pageContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
flexGrow: '1',
},
}));
function Login() {
const authCtx = useContext(AuthContext);
const { sendRequest, status, data: userData, error } = useHttp(login);
const loginHandler = (email, password) => {
sendRequest({ email, password });
};
if (status === 'pending') {
console.log('making request');
}
if (status === 'completed' && userData) {
console.log('updateContext');
authCtx.login(userData);
}
if (status === 'completed' && error) {
console.log(error);
}
const classes = useStyles();
return (
<div className={classes.pageWrapper}>
<Container maxWidth="md" className={classes.pageContainer}>
<LoginForm status={status} onLoginHandler={loginHandler} />
</Container>
</div>
);
}
export default Login;登入api -
export const login = async ({ email, password }) => {
let config = {
method: 'post',
url: `${BACKEND_URL}/api/auth/`,
headers: { 'Content-Type': 'application/json' },
data: {
email: email,
password: password,
},
};
const response = await axios(config);
return response.data;
};八月的情况-
import React, { useState } from 'react';
import { useEffect, useCallback } from 'react';
import {
getUser,
removeUser,
saveUser,
getExpirationTime,
clearExpirationTime,
setExpirationTime,
} from '../utils/local-storage';
const AuthContext = React.createContext({
token: '',
isLoggedIn: false,
login: () => {},
logout: () => {},
});
let logoutTimer;
const calculateRemainingTime = expirationTime => {
const currentTime = new Date().getTime();
const adjExpirationTime = new Date(expirationTime).getTime();
const remainingDuration = adjExpirationTime - currentTime;
return remainingDuration;
};
const retrieveStoredToken = () => {
const storedToken = getUser();
const storedExpirationDate = getExpirationTime();
const remainingTime = calculateRemainingTime(storedExpirationDate);
if (remainingTime <= 60) {
removeUser();
clearExpirationTime();
return null;
}
return {
token: storedToken,
duration: remainingTime,
};
};
export const AuthContextProvider = ({ children }) => {
const tokenData = retrieveStoredToken();
let initialToken = '';
if (tokenData) {
initialToken = tokenData.token;
}
const [token, setToken] = useState(initialToken);
const userIsLoggedIn = !!token;
const logoutHandler = useCallback(() => {
setToken(null);
removeUser();
clearExpirationTime();
if (logoutTimer) {
clearTimeout(logoutTimer);
}
}, []);
const loginHandler = ({ token, user }) => {
console.log('login Handler runs');
console.log(token, user.expiresIn);
setToken(token);
saveUser(token);
setExpirationTime(user.expiresIn);
const remainingTime = calculateRemainingTime(user.expiresIn);
logoutTimer = setTimeout(logoutHandler, remainingTime);
};
useEffect(() => {
if (tokenData) {
console.log(tokenData.duration);
logoutTimer = setTimeout(logoutHandler, tokenData.duration);
}
}, [tokenData, logoutHandler]);
const user = {
token,
isLoggedIn: userIsLoggedIn,
login: loginHandler,
logout: logoutHandler,
};
return <AuthContext.Provider value={user}>{children}</AuthContext.Provider>;
};
export default AuthContext;问题是当我在Login组件中调用AuthContext的AuthContext函数时,Login组件重新呈现并且这个登录函数处于无限循环中。我做错了什么?
从几个小时以来,我对这个问题的反应和坚持都是新的。
发布于 2021-06-25 22:27:18
我想我知道这是什么了。
你通过钩子带来了一堆组件状态。每当authCtx、sendRequest、status、data和error更改时,组件就会重新呈现.避免在州内关闭。闭包触发不必要的重呈现。
function Login() {
const authCtx = useContext(AuthContext);
const { sendRequest, status, data: userData, error } = useHttp(login);尝试查找可能导致重呈现的所有闭包,并确保组件不依赖于它们。
编辑:
本韦斯特是对的-你也有副作用发生在渲染,这是错误的。
当功能组件的主体中有类似的内容时:
if (status === 'completed' && userData) {
console.log('updateContext');
authCtx.login(userData);
}将其更改为:
useEffect(() => {
if (status === 'completed' && userData) {
console.log('updateContext');
authCtx.login(userData);
}
}, [status, userData]); //the function in arg 1 is called whenever these dependencies change我对您的代码做了一系列更改:
只剩下两个文件了。我还写了其他的东西。
我对useContext()不太熟悉,所以我不能说您是否正确地使用了它。
Login.js
import { useContext, useEffect } from 'react';
import { makeStyles } from '@material-ui/core';
import Container from '@material-ui/core/Container';
import LoginForm from '../components/login/LoginForm';
import AuthContext from '../store/auth-context';
const useStyles = makeStyles(theme => ({
pageWrapper: {
height: '100vh',
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.palette.background.default,
},
pageContainer: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
flexGrow: '1',
},
}));
function httpReducer(state, action) {
switch (action.type) {
case 'SEND':
return {
data: null,
error: null,
status: 'pending',
};
case 'SUCCESS':
return {
data: action.responseData,
error: null,
status: 'completed',
};
case 'ERROR':
return {
data: null,
error: action.errorMessage,
status: 'completed',
};
default:
return state;
}
}
function Login() {
const [httpState, dispatch] = useReducer(httpReducer, {
status: startWithPending ? 'pending' : null,
data: null,
error: null,
});
const sendRequest = async requestData => {
dispatch({ type: 'SEND' });
try {
let config = {
method: 'post',
url: `${BACKEND_URL}/api/auth/`,
headers: { 'Content-Type': 'application/json' },
data: {
email: requestData.email,
password: requestData.password,
},
};
const response = await axios(config);
dispatch({ type: 'SUCCESS', responseData: response.data });
} catch (error) {
dispatch({
type: 'ERROR',
errorMessage: error.response.data.message || 'Something went wrong!',
});
}
};
const authCtx = useContext(AuthContext);
const loginHandler = (email, password) => {
sendRequest({ email, password });
};
useEffect(() => {
if (httpState.status === 'pending') {
console.log('making request');
}
}, [httpState.status]);
useEffect(() => {
if (httpState.status === 'completed' && httpState.data) {
console.log('updateContext');
authCtx.login(httpState.data);
}
}, [httpState.status, httpState.data]);
useEffect(() => {
if (httpState.status === 'completed' && httpState.error) {
console.log(httpState.error);
}
}, [httpState.status, httpState.error]);
const classes = useStyles();
return (
<div className={classes.pageWrapper}>
<Container maxWidth="md" className={classes.pageContainer}>
<LoginForm status={httpState.status} onLoginHandler={loginHandler} />
</Container>
</div>
);
}
export default Login;AuthContext.js
import React, { useState } from 'react';
import { useEffect } from 'react';
import {
getUser,
removeUser,
saveUser,
getExpirationTime,
clearExpirationTime,
setExpirationTime,
} from '../utils/local-storage';
const AuthContext = React.createContext({
token: '',
isLoggedIn: false,
login: () => {},
logout: () => {},
});
const calculateRemainingTime = expirationTime => {
const currentTime = new Date().getTime();
const adjExpirationTime = new Date(expirationTime).getTime();
const remainingDuration = adjExpirationTime - currentTime;
return remainingDuration;
};
// is this asynchronous?
const retrieveStoredToken = () => {
const storedToken = getUser();
const storedExpirationDate = getExpirationTime();
const remainingTime = calculateRemainingTime(storedExpirationDate);
if (remainingTime <= 60) {
removeUser();
clearExpirationTime();
return null;
}
return {
token: storedToken,
duration: remainingTime,
};
};
export const AuthContextProvider = ({ children }) => {
const [tokenData, setTokenData] = useState(null);
const [logoutTimer, setLogoutTimer] = useState(null);
useEffect(() => {
const tokenData_ = retrieveStoredToken(); //is this asynchronous?
if (tokenData_) {
setTokenData(tokenData_);
}
}, []);
const userIsLoggedIn = !!(tokenData && tokenData.token);
const logoutHandler = () => {
setTokenData(null);
removeUser();//is this asynchronous?
clearExpirationTime();
if (logoutTimer) {
clearTimeout(logoutTimer);
//clear logoutTimer state here? -> setLogoutTimer(null);
}
};
const loginHandler = ({ token, user }) => {
console.log('login Handler runs');
console.log(token, user.expiresIn);
setTokenData({ token });
saveUser(token);
setExpirationTime(user.expiresIn);
const remainingTime = calculateRemainingTime(user.expiresIn);
setLogoutTimer(setTimeout(logoutHandler, remainingTime));
};
useEffect(() => {
if (tokenData && tokenData.duration) {
console.log(tokenData.duration);
setLogoutTimer(setTimeout(logoutHandler, tokenData.duration));
}
}, [tokenData]);
const user = {
token: tokenData.token,
isLoggedIn: userIsLoggedIn,
login: loginHandler,
logout: logoutHandler,
};
return <AuthContext.Provider value={user}>{children}</AuthContext.Provider>;
};
export default AuthContext;https://stackoverflow.com/questions/68137071
复制相似问题