【发布时间】:2021-09-09 04:57:50
【问题描述】:
我正在尝试向我的应用添加身份验证并使用 React Context API 维护身份验证状态。
我正在使用自定义钩子 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,我需要离开这个页面并更新我的身份验证上下文。
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;
问题是当我在登录组件中调用我的 AuthContext 的 loginHandler 函数时,登录组件重新呈现并且此登录函数进入无限循环。我做错了什么?
我是 React 的新手,从现在开始就一直在这个问题上纠结。
【问题讨论】:
-
看起来
retrieveStoredToken()有副作用,因此不应在渲染中调用它。
标签: javascript reactjs context-api