Remix:如何在子路由中获取父路由Action的返回数据?
如何在Remix子路由中复用父路由Action已获取的数据
下面提供三种可行方案,根据你的场景选择合适的方式:
方案1:通过父组件Outlet传递上下文
如果子路由是通过父路由的<Outlet>渲染的,可以直接把父路由Action返回的数据通过Outlet的context传递给子路由组件,无需额外请求。
父路由(parent.tsx)代码:
import { Outlet, useActionData } from '@remix-run/react'; import type { ActionFunction } from '@remix-run/node'; export const action: ActionFunction = async ({ request }) => { // 模拟获取数据 const data = await fetch('https://api.example.com/parent-data').then(res => res.json()); return data; }; export default function ParentRoute() { // 获取Action返回的数据 const parentActionData = useActionData<typeof action>(); return ( <div> {/* 通过Outlet的context传递数据给子路由 */} <Outlet context={{ parentData: parentActionData }} /> </div> ); }
子路由(parent.$id.tsx)代码:
import { useOutletContext } from '@remix-run/react'; // TypeScript可选:定义上下文类型 type ParentContext = { parentData: Awaited<ReturnType<typeof import('./parent').action>>; }; export default function ChildRoute() { // 从Outlet上下文获取父路由Action的数据 const { parentData } = useOutletContext<ParentContext>(); return ( <div> <h3>子路由展示父数据:</h3> <pre>{JSON.stringify(parentData, null, 2)}</pre> </div> ); }
注意:这种方式仅适用于父路由Action触发后的同页面渲染场景(比如表单提交后页面不跳转,直接渲染子路由)。如果用户刷新子路由页面,父路由的Action不会重新执行,useActionData会返回undefined,此时子路由拿不到数据。
方案2:用Session临时存储数据
如果需要跨请求复用父路由Action的数据(比如用户提交表单后跳转到子路由页面),可以把数据存入Session,子路由从Session中读取。
父路由(parent.tsx)代码:
import { json } from '@remix-run/node'; import type { ActionFunction } from '@remix-run/node'; export const action: ActionFunction = async ({ request, context }) => { const data = await fetch('https://api.example.com/parent-data').then(res => res.json()); // 将数据存入Session const session = await context.getSession(); session.set('parentActionData', data); return json(data, { headers: { 'Set-Cookie': await context.commitSession(session), }, }); };
子路由(parent.$id.tsx)代码:
import { json } from '@remix-run/node'; import { useLoaderData } from '@remix-run/react'; import type { LoaderFunction } from '@remix-run/node'; export const loader: LoaderFunction = async ({ context }) => { // 从Session中读取数据 const session = await context.getSession(); const parentData = session.get('parentActionData'); // 可选:读取后删除Session中的数据,避免长期存储 session.unset('parentActionData'); return json({ parentData }, { headers: { 'Set-Cookie': await context.commitSession(session), }, }); }; export default function ChildRoute() { const { parentData } = useLoaderData<typeof loader>(); return ( <div> <h3>子路由从Session获取父数据:</h3> <pre>{JSON.stringify(parentData, null, 2)}</pre> </div> ); }
注意:Session存储在Cookie中,数据大小有限制(一般不超过4KB),不适合存储大型数据。
方案3:提取共享缓存函数复用数据
如果数据是通用的、允许短期缓存,可以把数据获取逻辑抽成带缓存的共享函数,父路由Action和子路由Loader都调用这个函数,避免重复请求。
共享数据函数(utils/fetchParentData.ts):
// 内存缓存,单实例有效;多实例部署建议用Redis等分布式缓存 let cachedData: any = null; let cacheExpiry: number | null = null; export async function fetchParentData() { const now = Date.now(); // 如果缓存存在且未过期,直接返回缓存数据 if (cachedData && cacheExpiry && now < cacheExpiry) { return cachedData; } // 重新获取数据 const data = await fetch('https://api.example.com/parent-data').then(res => res.json()); // 设置缓存(比如1分钟过期) cachedData = data; cacheExpiry = now + 60 * 1000; return data; }
父路由(parent.tsx)代码:
import type { ActionFunction } from '@remix-run/node'; import { fetchParentData } from '~/utils/fetchParentData'; export const action: ActionFunction = async () => { const data = await fetchParentData(); return data; };
子路由(parent.$id.tsx)代码:
import { json } from '@remix-run/node'; import { useLoaderData } from '@remix-run/react'; import type { LoaderFunction } from '@remix-run/node'; import { fetchParentData } from '~/utils/fetchParentData'; export const loader: LoaderFunction = async () => { const parentData = await fetchParentData(); return json({ parentData }); }; export default function ChildRoute() { const { parentData } = useLoaderData<typeof loader>(); return ( <div> <h3>子路由通过缓存函数获取父数据:</h3> <pre>{JSON.stringify(parentData, null, 2)}</pre> </div> ); }
注意:内存缓存仅在单个服务器实例中有效,如果是多实例部署(比如云服务器集群),建议使用Redis等分布式缓存来保证缓存一致性。
内容的提问来源于stack exchange,提问作者arlioli03
相关产品推荐
相关产品推荐

