useEffect依赖location.state初始渲染未触发,如何向Outlet子组件传异步数据?
解决方案:从父组件向Outlet默认路由传递异步数据
问题根源
初始渲染Test.js时,是直接匹配路由规则进入的,并非通过带state的Link跳转,因此location.state为空;只有通过Link导航(包括从其他路由返回)时,state才会被携带。以下是几种可靠的实现方案:
方案一:使用React Context传递(推荐,灵活通用)
适合需要在组件树中共享数据的场景,异步数据加载完成后自动同步到子组件。
1. 在App.js中创建并提供Context
import { createContext, useState, useEffect } from 'react'; import { Outlet, Link } from 'react-router-dom'; // 创建Context export const TestDataContext = createContext(null); function App() { const [asyncData, setAsyncData] = useState(null); // 模拟异步数据请求 useEffect(() => { setTimeout(() => { setAsyncData({ id: 1, content: '异步加载的测试数据' }); }, 1000); }, []); return ( <div> <Link to="">Render test</Link> <Link to="/dummy" style={{ marginLeft: '10px' }}>Dummy</Link> {/* 用Provider包裹Outlet,传递数据 */} <TestDataContext.Provider value={asyncData}> <Outlet /> </TestDataContext.Provider> </div> ); } export default App;
2. 在Test.js中获取Context数据
import { useContext } from 'react'; import { TestDataContext } from './App'; function Test() { const testData = useContext(TestDataContext); // 处理数据未加载完成的状态 if (!testData) { return <div>加载中...</div>; } return <div>获取到的数据:{testData.content}</div>; } export default Test;
方案二:使用React Router的Loader函数(路由级数据传递)
React Router v6+ 官方推荐的路由专属数据获取方式,数据在路由渲染前加载,避免组件内的异步请求逻辑。
1. 配置路由时添加Loader
// 路由配置文件(如routes.js) import { createBrowserRouter } from 'react-router-dom'; import App from './App'; import Test from './Test'; import Dummy from './Dummy'; // 异步获取数据的Loader函数 async function testLoader() { // 模拟API请求 const res = await new Promise(resolve => { setTimeout(() => resolve({ id: 1, content: '从Loader获取的异步数据' }), 1000); }); return res; } const router = createBrowserRouter([ { path: '/', element: <App />, children: [ // 默认路由绑定Loader { index: true, element: <Test />, loader: testLoader }, { path: 'dummy', element: <Dummy /> }, ], }, ]); export default router;
2. 在Test.js中获取Loader数据
import { useLoaderData } from 'react-router-dom'; function Test() { const testData = useLoaderData(); if (!testData) { return <div>加载中...</div>; } return <div>获取到的数据:{testData.content}</div>; } export default Test;
方案三:通过navigate主动补充state(局限性大,不推荐)
仅适用于不需要保留刷新后数据的场景,因为location.state在页面刷新后会丢失。
// App.js import { useState, useEffect } from 'react'; import { Outlet, Link, useLocation, useNavigate } from 'react-router-dom'; function App() { const [asyncData, setAsyncData] = useState(null); const location = useLocation(); const navigate = useNavigate(); useEffect(() => { setTimeout(() => { setAsyncData({ id: 1, content: '异步数据' }); }, 1000); }, []); // 数据加载完成后,若当前是默认路由且无state,主动跳转补充state useEffect(() => { if (asyncData && location.pathname === '/' && !location.state) { navigate('/', { state: { data: asyncData }, replace: true }); } }, [asyncData, location, navigate]); return ( <div> <Link to="" state={{ data: asyncData }}>Render test</Link> <Link to="/dummy" style={{ marginLeft: '10px' }}>Dummy</Link> <Outlet /> </div> ); } export default App;
方案对比
- Context方案:灵活通用,支持多组件共享数据,页面刷新后重新请求数据,无额外路由依赖。
- Loader方案:路由级数据获取,数据在路由渲染前加载,逻辑与路由绑定,适合路由专属数据。
- navigate补充state:局限性大,刷新后数据丢失,仅适合临时场景。
内容的提问来源于stack exchange,提问作者KvS
相关产品推荐
相关产品推荐

