You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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逻辑,可以把它改成嵌套路由:

  1. 将原_index.tsx调整为父路由(比如重命名为routes/index.tsx),确保组件包含Outlet;
  2. 创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 12:11:00