页面刷新后未读取LocalStorage用户详情,重复请求Cognito问题排查
使用Context Provider管理用户状态,登录成功后已将用户详情存入state与LocalStorage,但页面刷新时,Header组件并未从LocalStorage读取数据,反而重新向Cognito发起请求。
相关代码
Context中获取用户详情的函数
// get user details functionality const getUserDetails = async (accessToken) => { let postData = { 'accessToken': accessToken }; let result = {}; let code = -1; let status = '' let statusText = '' let message = '' await axios.post(Constants.URL_GET_USER_DETAILS, postData, {headers: headerData}) .then( data => { code = data.data.code; status = data.data.status; statusText = data.data.statusText; message = data.data.message; //If successfully fetched user details if (code === 200) { let userDetails = new CognitoUserDetails(message); // save in the use state setUserDetails(loggedUserDetails => userDetails) // save in the local storage setItemInLocalStorage( Constants.LOCAL_STORAGE_USER_DETAILS, userDetails); result = message; } else { } }).catch( error => { }); return result; }
Header组件的useEffect逻辑
// use effect for user details useEffect( () => { let userDetails = getUserDetailsFromStore(); //Check if the user details are stored in state or local storage. if ( !Utilities.IsEmptyObject(userDetails) ) { // if they are, read from it console.log("details read from store"); } else { //If it is not, send the request and get the user details let token = getUserTokenFromStore(); //If the token is there if ( !Utilities.IsEmptyObject(token) ) { } else { //else log out the user - as the token is not there, means the user has not logged in } userDetailsRequest(token.AccessToken) .then( data => { console.log("success::: user details request"); }); } }, [loggedUserDetails]);
从state/LocalStorage读取用户详情的函数
//Get user details from the state or from the local storage const getUserDetailsFromStore = () => { let token = {}; //If the user details are the state if ( !Utilities.IsEmptyObject(loggedUserDetails) ) { //Get it from the state token = loggedUserDetails; } else { // else check if it is available in local storage let item = getItemFromLocalStorage( Constants.LOCAL_STORAGE_USER_DETAILS ); if ( Utilities.IsEmptyObject(item) ) { //Get it from local storage token = item; } else { console.log("getUserDetailsFromStore:::: not in local storage"); } } return token; }
getUserDetailsFromStore逻辑完全写反
看LocalStorage读取分支:当前代码是如果item是空对象,才把token赋值为item,这和预期逻辑相反。正确逻辑应该是如果item不是空对象,才将token赋值为item,否则才输出"不在LocalStorage"的日志。这个错误直接导致LocalStorage有数据时,函数返回的还是空对象,触发重新请求。Context初始化未同步LocalStorage数据到state
页面刷新后,Context的loggedUserDetails会重置为空对象。如果Context在初始化时没有主动从LocalStorage读取数据并更新state,那么loggedUserDetails始终为空,Header的useEffect会一直走请求分支。LocalStorage序列化/反序列化异常
检查setItemInLocalStorage和getItemFromLocalStorage的实现:是否把CognitoUserDetails实例正确序列化为JSON字符串存入,读取时又正确反序列化为实例?如果序列化丢失数据,或者反序列化后得到的是普通对象导致Utilities.IsEmptyObject误判,也会触发错误分支。Utilities.IsEmptyObject函数准确性
确认该函数是否能正确判断空对象:比如是否把{}视为空,把包含属性的对象视为非空。如果函数逻辑错误(比如误判有数据的对象为空),会导致代码错误地进入请求分支。useEffect初始执行时机问题
页面刷新时loggedUserDetails初始为空,useEffect会立刻执行。如果此时LocalStorage数据存在但未同步到state,函数返回空对象后就会触发请求,后续即使同步state也不会重新执行useEffect(因为依赖的loggedUserDetails已经从空变为有值,但此时请求已经发起)。
内容的提问来源于stack exchange,提问作者user1268139

