You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 11:21:29