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

React Context中useEffect加载异常,组件持续显示Loading的解决方法

解决React Context中认证逻辑导致的持续Loading问题

核心问题分析

你的UserContextProvider在state.loading为true时,直接返回了Loading组件,完全没有渲染userContext.Provider。这就导致所有子组件(包括Landing页面)根本无法获取到Context中的状态,自然会一直显示loading。同时Landing页面只检查user是否存在,没有结合Context的loading状态判断,逻辑也不够严谨。


修正后的Context代码

修改UserContextProvider,确保无论loading状态如何,都要渲染Provider,把状态通过value传递给子组件:

export const UserContextProvider = ({children}) => {
    const [state, dispatch] = useReducer(authorizeUser, {
        loading: true,
        user: null // 初始化user为null
    })

    useEffect(() => {
        const userToken = localStorage.getItem('user')
        if(userToken){
            const decoded = jwtDecode(userToken)
            // 传递完整解码后的用户信息,而非仅_id
            dispatch({type: 'LOGIN', payload: decoded})
        }
        // 无论是否存在用户,最终关闭loading
        dispatch({type: 'SET_LOADING', payload: false})
    }, [])

    // 始终渲染Provider,将状态传递给子组件
    return(
        <userContext.Provider value={{...state, dispatch}}>
            {children}
        </userContext.Provider>
    )
}

// 确保reducer正确处理状态更新
function authorizeUser(state, action){
    switch(action.type){
        case 'LOGIN':
            return {
                ...state,
                user: action.payload,
                loading: false // 登录成功后直接关闭loading
            }
        case 'SET_LOADING':
            return {
                ...state,
                loading: action.payload
            }
        case 'LOGOUT':
            return {
                ...state,
                user: null
            }
        default:
            return state
    }
}

修正后的Landing页面代码

子组件从Context中获取loading和user状态,结合两者判断显示内容:

import { useContext } from 'react'
import { userContext } from './path-to-your-context'

const LandingExample = () => {
    const { loading, user } = useContext(userContext)
    // 你的其他逻辑:handleSubmit、handlePhoto等

    // 优先判断loading状态
    if(loading){
        return <div><h1>Loading...</h1></div>
    }

    // loading结束后判断用户是否存在
    if(!user){
        // 这里可跳转登录页或显示登录入口
        return <div><h1>请先登录</h1></div>
    }

    return (
        <div>
            <div>
                <div>
                    <h1>Welcome {user.name}</h1>
                    <UserLogout></UserLogout>
                </div>
                <form onSubmit={handleSubmit}>
                    <input
                        type='file'
                        name='photo'
                        onChange={handlePhoto}
                    />
                    <input type='submit' />
                </form>
            </div>
            <UserImageGallery image={image} />
        </div> 
    ) 
}

export default LandingExample

关键注意点

  • Provider必须始终渲染:不能在Provider组件内拦截子组件渲染,应将状态传递给子组件,由子组件自行处理UI逻辑。
  • reducer状态更新要完整:每个action类型需正确更新state的对应字段,比如登录成功时同时设置user和loading状态。
  • 结合双状态判断:先确认loading结束,再判断用户是否存在,避免逻辑漏洞。

内容的提问来源于stack exchange,提问作者Jason Hymes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:56:35