React18使用<Navigate/>跳转后Home组件需手动刷新才可渲染问题
问题描述
- 技术栈:React 18 + Redux + react-router-dom
- 异常现象:登录成功后页面短暂显示首页内容,随后展示Loader加载组件,接着页面完全白屏;仅手动刷新页面后Home组件才可正常渲染
- 现有实现:Home组件树存在多层嵌套,采用逐层向子组件传递props的方式实现数据传递,怀疑问题与Redux的action派发逻辑相关,目标是实现Home组件首次跳转即可正常加载,无需手动刷新
- 白屏时控制台告警:

- 手动刷新正常渲染时控制台告警:

问题根因
- 鉴权状态初始化时序缺失:App.js中
loadUser鉴权请求为异步执行,未对请求过程增加加载态兜底,首次渲染时Redux中isAuthenticated状态未初始化完成,路由判断逻辑提前执行,出现跳转闪烁、组件反复挂载卸载的问题。 - 渲染逻辑存在空值访问风险:Home组件未对Redux返回的状态做初始值兜底,登录跳转后组件挂载时,
posts/users等状态初始为undefined,渲染时直接访问post.image.url、post.owner.avatar.url这类深层属性会直接抛出类型错误,直接导致白屏。 - 副作用依赖项错误:Home组件中清理错误的useEffect依赖了未定义的
alert变量,导致副作用执行异常,错误状态无法正常清理,阻塞渲染流程。 - 冗余代码干扰:App.js中定义了未使用的
reload状态,Home组件引入了未使用的useState,存在无意义的内存占用。
修复方案
1. 修正App.js逻辑,增加鉴权加载态拦截
鉴权请求完成前不做路由判断,从根源避免跳转闪烁问题,修正后代码如下:
import { Fragment, useEffect } from 'react'; import {BrowserRouter as Router , Routes , Route , Navigate} from 'react-router-dom'; import './App.css'; import Header from './components/Header/Header'; import Home from './components/Home/Home.js'; import LandingPage from './components/Landing/Landing.js' import Login from './components/Login/Login'; import { useDispatch, useSelector } from 'react-redux'; import { loadUser } from './Actions/UserActions'; import Account from './components/Account/Account.js' import Loader from './components/Loader/Loader' function App() { const dispatch = useDispatch(); // 同步获取鉴权请求的加载状态 const { isAuthenticated, loading: authLoading } = useSelector(state=>state.user); useEffect(() => { dispatch(loadUser()); }, [dispatch]) // 鉴权请求未完成时展示全局加载态,禁止路由提前渲染 if (authLoading) return <Loader /> return ( <Fragment> <Router> {isAuthenticated && <Header/>} <Routes> <Route path='/' element={!isAuthenticated? (<Navigate to='/login' replace={true} />): (<Home/>)}/> <Route path='/welcome' element={<LandingPage/>}/> <Route path='/login' element={!isAuthenticated? <Login/> :(<Navigate to='/' replace={true}/>)}/> <Route path='/account' element={!isAuthenticated? (<Navigate to='/login' replace={true} />): <Account/>}/> </Routes> </Router> </Fragment> ); } export default App;
2. 修正Home组件逻辑,消除空值访问风险
移除无效依赖、增加状态兜底、通过可选链避免深层属性访问报错,修正后代码如下:
import React, { useEffect } from 'react' import './Home.css' import User from '../User/User.js' import Post from '../Post/Post.js' import { useDispatch, useSelector } from 'react-redux' import { getAllUsers, getPosts } from '../../Actions/UserActions' import Loader from '../Loader/Loader' import { Typography } from '@mui/material' import Metadata from '../Metadata/Metadata' const Home = () => { const dispatch=useDispatch(); // 为Redux状态增加空值兜底,避免返回undefined const { loading = false, posts = [], error} = useSelector(state =>state.getPosts || {}) const { usersLoading = false , users = [], userError} =useSelector(state=>state.allUsers || {}) const { error: likeError, message } = useSelector((state) => state.like || {}); useEffect(() => { // 避免重复派发请求 if (posts.length === 0) dispatch(getPosts()); if (users.length === 0) dispatch(getAllUsers()); }, [dispatch, posts.length, users.length]) useEffect(() => { if (error) dispatch({ type: "clearErrors" }); if (likeError) dispatch({ type: "clearErrors" }); // 移除未定义的alert依赖,保证副作用正常执行 }, [error, message, likeError, dispatch]); if (loading || usersLoading) return <Loader /> return ( <div className="home"> <Metadata title='Civilised' /> <div className="homeleft"> { posts.length>0 ? posts.map((post)=> // 通过可选链访问深层属性,避免空值报错 (<Post key={post?._id} postId={post?._id} caption={post?.caption} postImage={post?.image?.url} likes={post?.likes || []} isLiked={false} comments={post?.comments || []} ownerImage={post?.owner?.avatar?.url} ownerName={post?.owner?.name} ownerId={post?.owner?._id} />) ): (<Typography variant='h6'>No posts</Typography>) } </div> <div className="homeright"> { users.length>0 ? users.map((user)=>( <User key={user?._id} userId={user?._id} name={user?.name} avatar={'https://media-exp1.licdn.com/dms/image/C5603AQHlHovW8nMkFA/profile-displayphoto-shrink_200_200/0/1610020467592?e=2147483647&v=beta&t=vBPLqLRHM1Py_hRw7vSbT86TKE7UREGqCFyvoYGyJoc'} /> )) : null } </div> </div> ) } export default Home
3. 校验Redux切片初始值配置
确保所有关联的Redux切片都配置了合理的初始状态,从数据源层面避免undefined问题,参考配置如下:
// user切片初始值 const userInitialState = { isAuthenticated: false, loading: false, user: null, error: null } // posts切片初始值 const postsInitialState = { loading: false, posts: [], error: null } // allUsers切片初始值 const usersInitialState = { usersLoading: false, users: [], userError: null } // like切片初始值 const likeInitialState = { loading: false, message: null, error: null }
修复后效果
修复后页面流程为:登录/页面加载时先等待鉴权请求完成,期间展示全局加载态;鉴权通过后正常渲染Home组件,拉取帖子、用户数据时展示局部加载态;数据返回后直接渲染内容,不会出现空值报错,首次跳转即可正常展示首页,无需手动刷新。
内容的提问来源于stack exchange,提问作者SWORD ASH
相关产品推荐
相关产品推荐

