Remix同路由下子组件useLoaderData返回null,图片加载失败求助

我按照教程实现Remix服务端图片加载,代码如下:
import { Outlet, useLoaderData } from '@remix-run/react'; export async function loader() { const response = await fetch('https://picsum.photos/500/300'); return { imageUrl: response.url, }; } export default function RandomImageLayout() { const imageData = useLoaderData<typeof loader>(); return ( <> <img src={imageData.imageUrl} alt="Random Image" style={{ maxWidth: '100%' }} /> <Outlet /> </> ); }
这段代码在_index.tsx中通过useLoaderData可正常获取数据并显示图片,但将相同逻辑放到_index.tsx下的contact-us子组件中时,useLoaderData返回null,无法加载图片。我希望在同一路由下的多个组件中使用不同的useLoaderData获取各自的图片,该如何实现?或有其他可行方案吗?
解决方案
Remix的loader是路由级别的能力,只有路由文件(如_index.tsx、xxx.tsx这类路由组件)能导出loader,普通子组件无法直接通过useLoaderData获取自身的loader数据。针对你的需求,有三种可行方案:
方案1:父路由批量获取,通过props传递给子组件
在父路由的loader中并行请求所有需要的图片URL,再将对应数据通过props传给子组件:
// _index.tsx import { Outlet, useLoaderData } from '@remix-run/react'; import ContactUs from './contact-us'; export async function loader() { // 并行请求多个图片,提升性能 const [mainImgRes, contactImgRes] = await Promise.all([ fetch('https://picsum.photos/500/300'), fetch('https://picsum.photos/400/200') ]); return { mainImageUrl: mainImgRes.url, contactImageUrl: contactImgRes.url }; } export default function Index() { const data = useLoaderData<typeof loader>(); return ( <> <img src={data.mainImageUrl} alt="主图" style={{ maxWidth: '100%' }} /> <ContactUs imageUrl={data.contactImageUrl} /> <Outlet /> </> ); }
子组件直接接收props使用:
// contact-us.tsx export default function ContactUs({ imageUrl }: { imageUrl: string }) { return <img src={imageUrl} alt="联系我们图片" style={{ maxWidth: '100%' }} />; }
方案2:用useFetcher让子组件独立发起请求
如果不想在父路由提前获取所有数据,可以通过useFetcher让子组件自己调用服务端接口获取图片:
首先创建一个单独的API路由用于返回contact图片URL:
// routes/api/contact-image.tsx export async function loader() { const res = await fetch('https://picsum.photos/400/200'); return new Response(JSON.stringify({ imageUrl: res.url }), { headers: { 'Content-Type': 'application/json' } }); }
然后在子组件中使用useFetcher请求这个接口:
// contact-us.tsx import { useFetcher } from '@remix-run/react'; import { useEffect } from 'react'; export default function ContactUs() { const fetcher = useFetcher<{ imageUrl: string }>(); // 组件挂载时触发请求 useEffect(() => { fetcher.load('/api/contact-image'); }, [fetcher]); if (fetcher.loading) return <div>加载中...</div>; if (!fetcher.data) return null; return <img src={fetcher.data.imageUrl} alt="联系我们图片" style={{ maxWidth: '100%' }} />; }
方案3:将子组件升级为嵌套路由
如果contact-us需要完全独立的loader逻辑,可以把它改成嵌套路由:
- 将原
_index.tsx调整为父路由(比如重命名为routes/index.tsx),确保组件包含Outlet; - 创建
routes/index/contact-us.tsx作为嵌套路由,在这个文件中编写独立的loader和组件:
// routes/index/contact-us.tsx import { useLoaderData } from '@remix-run/react'; export async function loader() { const res = await fetch('https://picsum.photos/400/200'); return { imageUrl: res.url }; } export default function ContactUs() { const data = useLoaderData<typeof loader>(); return <img src={data.imageUrl} alt="联系我们图片" style={{ maxWidth: '100%' }} />; }
此时访问对应嵌套路由路径时,该组件的loader会独立执行,useLoaderData能正常获取数据。
内容的提问来源于stack exchange,提问作者BigChungus
相关产品推荐
相关产品推荐

