手动刷新页面时如何让App.js的useEffect优先于子组件的useEffect执行
问题根因
- React组件挂载阶段的useEffect执行顺序为子组件优先于父组件,因此Dashboard的useEffect会比App的useEffect更早触发
- Firebase的
onAuthStateChanged是异步回调,即便App的useEffect先执行,回调也不会立刻返回结果,初始状态下store中的user就是未赋值的undefined - 页面内跳转时App已经完成初始化,user已经被赋值,因此不会触发问题;手动刷新时整个应用重新初始化,就会出现user为空的报错
解决方案
不需要额外引入中间件,以下两种方案都可以覆盖手动输入地址访问的场景:
方案一:路由层全局加鉴权拦截(推荐,一劳永逸)
从全局层面处理鉴权加载逻辑,所有需要登录的页面都可以复用该逻辑,还能避免未登录用户直接访问私密路由。
- 首先修改全局store的初始状态,新增
authLoading字段标记鉴权是否完成,user初始值设为null:
const initialState = { user: null, authLoading: true } // 对应的reducer新增SET_AUTH_LOADING的处理逻辑
- 修改App.js中的鉴权监听逻辑,第一次鉴权回调完成后标记加载结束:
function App() { const [{ user, authLoading }, dispatch] = useStateValue(); useEffect(() =>{ const unsubscribe = auth.onAuthStateChanged((authUser) => { if (authUser) { dispatch({ type: 'SET_USER', user: authUser }); } else { dispatch({ type: 'SET_USER', user: null }); } // 首次鉴权状态返回,结束加载状态 dispatch({ type: 'SET_AUTH_LOADING', authLoading: false }) }); return () => { unsubscribe(); } }, []); return ( <Router> <div className="app"> {/* 鉴权未完成时显示全局加载态,不渲染路由 */} {authLoading ? ( <div>加载中...</div> ) : ( <Switch> <Route path="/dashboard"> {/* 未登录用户直接跳转到登录页 */} {user ? <Dashboard /> : <Redirect to="/login" />} </Route> <Route path="/" > <Header /> <Home /> </Route> </Switch> )} </div> </Router> ); }
方案二:Dashboard组件内兼容处理(改动最小,适合快速修复)
直接修改Dashboard的useEffect逻辑,新增对user的判断和监听:
const Dashboard = () => { const [{user}] = useStateValue(); // 将user加入依赖数组,user更新后会重新触发逻辑 useEffect(() => { // 只有user存在时才调用接口 if (user) { console.log(`user is ${user}`); getItemsByUser(user); } }, [user]) const getItemsByUser = async (user) => { const items = await firebaseService.getUserProducts(user); console.log(items[0]) } // user未初始化完成时显示组件内加载态 if (!user) return <div>加载中...</div> }
内容的提问来源于stack exchange,提问作者newnick988888
相关产品推荐
相关产品推荐

