React JWT鉴权调用useJWTAuth报无法解构user属性错误
React JWT登录模板运行时报错排查
本人是React初学者,近期使用下载模板内置的JWT登录方法时触发运行时报错,暂未定位根因,相关信息如下:
涉及代码
AuthHooks.js
// ForJWT Auth import {getUserFromJwtAuth} from './helper/AuthHelper'; import { useJWTAuth, useJWTAuthActions, } from '../services/auth/jwt-auth/JWTAuthProvider'; export const useAuthUser = () => { const {user, isAuthenticated, isLoading} = useJWTAuth(); return { isLoading, isAuthenticated, user: getUserFromJwtAuth(user), }; }; export const useAuthMethod = () => { const {signInUser, signUpUser, logout} = useJWTAuthActions(); return { signInUser, logout, signUpUser, }; };
JWTAuthProvider.js
import React, {createContext, useContext, useEffect, useState} from 'react'; import PropTypes from 'prop-types'; import {useDispatch} from 'react-redux'; import { FETCH_ERROR, FETCH_START, FETCH_SUCCESS, } from '../../../../shared/constants/ActionTypes'; import jwtAxios, {setAuthToken} from './jwt-api'; const JWTAuthContext = createContext(); const JWTAuthActionsContext = createContext(); export const useJWTAuth = () => useContext(JWTAuthContext); export const useJWTAuthActions = () => useContext(JWTAuthActionsContext); const JWTAuthAuthProvider = ({children}) => { const [jwtAuthData, setJWTAuthData] = useState({ user: null, isAuthenticated: false, isLoading: true, }); const dispatch = useDispatch(); useEffect(() => { const getAuthUser = () => { const token = localStorage.getItem('token'); if (!token) { setJWTAuthData({ user: undefined, isLoading: false, isAuthenticated: false, }); return; } setAuthToken(token); jwtAxios .get('/auth') .then(({data}) => setJWTAuthData({ user: data, isLoading: false, isAuthenticated: true, }), ) .catch(() => setJWTAuthData({ user: undefined, isLoading: false, isAuthenticated: false, }), ); }; getAuthUser(); }, []); const signInUser = async ({email, password}) => { dispatch({type: FETCH_START}); try { const {data} = await jwtAxios.post('auth', {email, password}); localStorage.setItem('token', data.token); setAuthToken(data.token); const res = await jwtAxios.get('/auth'); setJWTAuthData({user: res.data, isAuthenticated: true, isLoading: false}); dispatch({type: FETCH_SUCCESS}); } catch (error) { setJWTAuthData(prev => ({ ...prev, isAuthenticated: false, isLoading: false, })); dispatch({type: FETCH_ERROR, payload: error.message}); } }; const signUpUser = async ({name, email, password}) => { dispatch({type: FETCH_START}); try { const {data} = await jwtAxios.post('users', {name, email, password}); localStorage.setItem('token', data.token); setAuthToken(data.token); const res = await jwtAxios.get('/auth'); setJWTAuthData({user: res.data, isAuthenticated: true, isLoading: false}); dispatch({type: FETCH_SUCCESS}); } catch (error) { setJWTAuthData(prev => ({ ...prev, isAuthenticated: false, isLoading: false, })); dispatch({type: FETCH_ERROR, payload: error.message}); } }; const logout = async () => { localStorage.removeItem('token'); setAuthToken(); setJWTAuthData({ user: null, isLoading: false, isAuthenticated: false, }); }; return ( <JWTAuthContext.Provider value={{ ...jwtAuthData, }}> <JWTAuthActionsContext.Provider value={{ signUpUser, signInUser, logout, }}> {children} </JWTAuthActionsContext.Provider> </JWTAuthContext.Provider> ); }; export default JWTAuthAuthProvider; JWTAuthAuthProvider.propTypes = { children: PropTypes.node.isRequired, };
报错信息
触发的报错为:
TypeError: Cannot destructure property 'user' of 'Object(...)(...)' as it is undefined
报错定位到代码行:
const {user, isAuthenticated, isLoading} = useJWTAuth();
根因与修复方案
报错本质是useJWTAuth()的返回值为undefined,对undefined做解构赋值直接触发异常。useJWTAuth是对useContext(JWTAuthContext)的封装,React中useContext返回undefined只有两种常见场景:
- 组件未被对应Provider包裹:调用
useJWTAuth/useAuthUser的组件不在JWTAuthAuthProvider的子树范围内。比如把Provider放在了路由出口的下级、或者根组件中App组件写在了Provider外层,会导致hook执行时还没接入Context。
修复:检查根组件渲染结构,确保所有用到鉴权hook的组件,都被JWTAuthAuthProvider包裹,示例正确结构:// 根组件index.js示例 import JWTAuthAuthProvider from './services/auth/jwt-auth/JWTAuthProvider'; import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <ReduxProvider store={store}> <JWTAuthAuthProvider> <App /> {/* 所有用到鉴权hook的组件都在App内部 */} </JWTAuthAuthProvider> </ReduxProvider> ) - Provider导入错误:
JWTAuthAuthProvider是默认导出,如果引入时错误使用命名导入(比如写import { JWTAuthAuthProvider } from 'xxx路径'),会导致导入的Provider值为undefined,包裹完全不生效。
修复:把Provider的导入语句改成默认导入形式:import JWTAuthAuthProvider from '对应JWTAuthProvider文件路径'。
额外优化点
原代码存在两个不影响当前报错、但可能引发后续异常的问题,已经在上面的修正代码中调整:
- 原代码中存储JWT鉴权状态的state变量命名为
firebaseData,和实际功能不符,容易引发后续维护混淆,已重命名为jwtAuthData - 原代码
signInUser、signUpUser的catch分支更新state时,直接展开闭包中捕获的旧state,可能因为闭包陈旧拿到过期状态,已改成函数式更新写法,保证拿到最新的state值。
内容的提问来源于stack exchange,提问作者Arun Laxman
相关产品推荐
相关产品推荐

