React(MERN)应用管理员登出时出现user为null的类型错误求助
React(MERN)应用管理员导航链接登出时报错问题解决
问题概述
在MERN栈React应用中实现导航栏/admin链接仅对管理员(user.isAdmin === true)可见的功能,正常状态下普通用户看不到该链接、管理员可正常访问,但管理员登出时会触发Uncaught TypeError: user is null错误,普通用户登出则无此问题。
现有代码片段
Navbar组件
import { useAuthContext } from '../hooks/useAuthContext' import { useIsAdmin } from '../hooks/useAdmin' const Navbar = () => { const [nav, setNav] = useState(false) const {logout} = useLogout() const {user} = useAuthContext() const isAdmin = useIsAdmin() // 渲染部分代码 { isAdmin ? <Link to='/admin'><li className='p-4 hover:text-[#00df9a] transition-all duration-500'>Admin {user.name}</li></Link> : <div></div> }
useAdmin自定义Hook
import { useEffect, useState } from 'react'; import { useAuthContext} from '../hooks/useAuthContext'; export function useIsAdmin() { const { user } = useAuthContext(); const [isAdmin, setIsAdmin] = useState(null); useEffect(() => { if (user) { setIsAdmin(user.isAdmin && user); } }, [user]); useEffect(() => { if (!user || user === null) { setIsAdmin(null); } }, [user]); return isAdmin; }
AuthContext
import { createContext, useReducer, useEffect } from 'react' export const AuthContext = createContext() export const authReducer = (state, action) => { switch (action.type) { case 'LOGIN': return { user: action.payload } case 'LOGOUT': window.localStorage.clear() return {user: null} case 'DELETE_USER': return {...state, user: state.user.filter(u => u.id !== action.payload )} default: return state } } export const AuthContextProvider = ({ children }) => { const [state, dispatch] = useReducer(authReducer, { user: null }) useEffect(() => { const user = JSON.parse(localStorage.getItem('user')) if (user) { dispatch({ type: 'LOGIN', payload: user }) } }, []) console.log('AuthContext state:', state) return ( <AuthContext.Provider value={{ ...state, dispatch }}> { children } </AuthContext.Provider> ) }
useLogout Hook
import { useAuthContext } from './useAuthContext' export const useLogout = () => { const { dispatch } = useAuthContext() const logout = () => { // 移除本地存储的用户信息 localStorage.removeItem('user') // 分发登出动作 dispatch({ type: 'LOGOUT' }) } return { logout } }
问题分析
- 报错核心原因:管理员登出时,
useIsAdmin的状态更新存在延迟,短暂保留了之前的用户对象引用,但此时AuthContext中的user已变为null。Navbar渲染时进入isAdmin分支,尝试访问user.name,触发空值类型错误。普通用户登出时isAdmin本身为null,不会进入该分支,因此无报错。 - useHook逻辑缺陷:
useIsAdmin返回的是user.isAdmin && user(即管理员用户对象)而非布尔值,导致判断逻辑混淆;同时两个useEffect监听同一user依赖,存在逻辑重复。
解决方案
方案1:修复useAdmin Hook,返回布尔值
修改useIsAdmin,使其返回明确的布尔值,避免状态不一致:
import { useEffect, useState } from 'react'; import { useAuthContext} from '../hooks/useAuthContext'; export function useIsAdmin() { const { user } = useAuthContext(); const [isAdmin, setIsAdmin] = useState(false); useEffect(() => { // 直接判断用户存在且为管理员 setIsAdmin(!!user && user.isAdmin === true); }, [user]); return isAdmin; }
方案2:渲染时添加空值保护
在Navbar中使用可选链操作符?.,确保访问user.name前user存在:
{ isAdmin ? <Link to='/admin'><li className='p-4 hover:text-[#00df9a] transition-all duration-500'>Admin {user?.name}</li></Link> : <div></div> }
方案3:优化登出逻辑(可选)
当前useLogout中localStorage.clear()会清除所有本地存储数据,仅移除user项即可:
export const useLogout = () => { const { dispatch } = useAuthContext() const logout = () => { localStorage.removeItem('user') dispatch({ type: 'LOGOUT' }) } return { logout } }
内容的提问来源于stack exchange,提问作者Tomasz
相关产品推荐
相关产品推荐

