routeLoader$()

ルートローダーは、サーバーでデータを読み込み、Qwik コンポーネント内で使用できるようにします。SPA/MPA ナビゲーションが発生したときにトリガーされるため、レンダリング中に Qwik コンポーネントによって呼び出すことができます。

ルートローダーは、src/routes フォルダー内の layout.tsx または index.tsx ファイル内でのみ宣言でき、必ずエクスポートする必要があります。

共通の再利用可能な routeLoaders$ を管理する場合は、この関数を既存のルートの 'layout.tsx' または 'index.tsx' ファイル内から再エクスポートすることが不可欠です。そうしないと、実行されなかったり、例外が発生したりします。詳細については、クックブック を参照してください。

src/routes/product/[productId]/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductDetails = routeLoader$(async (requestEvent) => {
  // This code runs only on the server, after every navigation
  const res = await fetch(`https://.../products/${requestEvent.params.productId}`);
  const product = await res.json();
  return product as Product;
});
 
export default component$(() => {
  // In order to access the `routeLoader$` data within a Qwik Component, you need to call the hook.
  const signal = useProductDetails(); // Readonly<Signal<Product>>
  return <p>Product name: {signal.value.product.name}</p>;
});

ルートローダーは、データベースまたは API からデータを取得するのに最適です。たとえば、CMS、天気 API、またはデータベースからのユーザーリストからデータを取得するために使用できます。

REST API を作成するために routeLoader$ を使用しないでください。その場合は、レスポンスヘッダーと本文を厳密に制御できる エンドポイント を使用することをお勧めします。

複数の routeLoader$

アプリケーション全体で複数の routeLoader$ を使用でき、任意の Qwik コンポーネントで使用できます。**同じファイルに複数の routeLoader$ を宣言することもできます**。

src/routes/layout.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
import { Footer } from '../components/footer.tsx';
 
export const useProductData = routeLoader$(async () => {
  const res = await fetch('https://.../product');
  const product = (await res.json()) as Product;
  return product;
});
 
export default component$(() => {
  const signal = useProductData();
  return (
    <main>
      <Slot />
      <Footer />
    </main>
  );
});
src/components/footer.tsx
import { component$ } from '@builder.io/qwik';
 
// Import the loader from the layout
import { useProductData } from '../routes/layout.tsx';
 
export const Footer = component$(() => {
  // Consume the loader data
  const signal = useProductData();
  return <footer>Product name: {signal.value.product.name}</footer>;
});

上記の例は、異なるファイルの 2 つの異なるコンポーネントで useProductData() を使用する方法を示しています。これは意図的な動作です。

src/routes/admin/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useLoginStatus = routeLoader$(async ({ cookie }) => {
  return {
    isUserLoggedIn: checkCookie(cookie),
  };
});
 
export const useCurrentUser = routeLoader$(async ({ cookie }) => {
  return {
    user: currentUserFromCookie(cookie),
  };
});
 
export default component$(() => {
  const loginStatus = useLoginStatus();
  const currentUser = useCurrentUser();
  return (
    <section>
      <h1>Admin</h1>
      {loginStatus.value.isUserLoggedIn ? (
        <p>Welcome {currentUser.value.user.name}</p>
      ) : (
        <p>You are not logged in</p>
      )}
    </section>
  );
});

上記の例は、同じファイルで 2 つの routeLoader$ を使用する方法を示しています。ユーザーがログインしているかどうかを確認するための汎用的な useLoginStatus ローダーと、ユーザーデータを取得するためのより具体的な useCurrentUser ローダーが使用されています。

RequestEvent

ミドルウェアエンドポイントonRequest および onGet と同様に、routeLoader$RequestEvent API にアクセスできます。これには、現在の HTTP リクエストに関する情報が含まれています。

この情報は、ローダーがリクエストに基づいて条件付きでデータを取得する必要がある場合、またはレスポンスステータス、ヘッダー、本文を手動で上書きする必要がある場合に役立ちます。

src/routes/product/[user]/index.tsx
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductRecommendations = routeLoader$(async (requestEvent) => {
  console.log('Request headers:', requestEvent.request.headers);
  console.log('Request cookies:', requestEvent.cookie);
  console.log('Request url:', requestEvent.url);
  console.log('Request method:', requestEvent.method);
  console.log('Request params:', requestEvent.params);
 
  // Use request details to fetch personalized data
  const res = fetch(`https://.../recommendations?user=${requestEvent.params.user}`);
  const recommendedProducts = (await res.json()) as Product[];
 
  return recommendedProducts;
});

別の routeLoader$ 内で routeLoader$ データにアクセスする

requestEvent.resolveValue メソッドを使用して、1 つの routeLoader$ のデータに別の routeLoader$ の内部からアクセスできます。

src/routes/product/[productId]/index.tsx
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductDetails = routeLoader$(async (requestEvent) => {
  const res = await fetch(`https://.../products/${requestEvent.params.productId}`);
  const product = await res.json();
  return product;
});
 
export const useProductRecommendations = routeLoader$(async (requestEvent) => {
  // Resolve the product details from the other loader
  const product = await requestEvent.resolveValue(useProductDetails);
 
  // Use the product details to fetch personalized data
  const res = fetch(`https://.../recommendations?product=${product.id}`);
  const recommendedProducts = (await res.json()) as Product[];
 
  return recommendedProducts;
});

同じ API を使用して、routeAction$ または globalAction$ のデータにアクセスすることもできます。

routeLoader$ での失敗値

routeLoader$ は、fail メソッドを使用して失敗値を返すことができます。これは、ローダーが期待されるデータの読み込みに成功しなかったことを示す特殊な値です。

さらに、fail 関数を使用すると、routeLoader$ は HTTP ステータスコードを上書きできます(たとえば、404 を返す)。

これは、ローダーが undefined ではない「エラー」値を返す必要がある場合、データの読み込みに失敗したことも示す必要がある場合に役立ちます。

src/routes/product/[productId]/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
 
export const useProductDetails = routeLoader$(async (requestEvent) => {
  const product = await db.from('products').filter('id', 'eq', requestEvent.params.productId);
  if (!product) {
    // Return a failed value to indicate that product was not found
    return requestEvent.fail(404, {
      errorMessage: 'Product not found'
    });
  }
  return {
    productName: product.name
  };
});
 
export default component$(() => {
  const product = useProductDetails();
 
  if (product.value.errorMessage) {
    // Render UI for failed value
    return <div>{product.value.errorMessage}</div>;
  }
  return <div>Product name: {product.value.productName}</div>;
});

ローダーでの相対 URL の処理

サーバー側の実行環境では、適切な機能のために相対 URL を絶対 URL に変換することが重要です。これは、useLocation() 関数の origin を相対 URL のプレフィックスにすることで実現できます。

絶対 URL の作成
import { component$ } from '@builder.io/qwik';
import { useLocation } from '@builder.io/qwik-city';
 
export default component$(() => {
  const location = useLocation();
  const relativeUrl = '/mock-data';
  const absoluteUrl = location.url.origin + relativeUrl;
 
  return (
    <section>
      <div>Relative URL: {relativeUrl}</div>
      <div>Absolute URL: {absoluteUrl}</div>
    </section>
  );
});

パフォーマンスに関する考慮事項

ルートローダーは、サーバー上で、すべてのナビゲーション後に実行されます。これは、ユーザーが SPA または MPA でページに移動するたびに実行され、ユーザーが同じページに移動する場合でも実行されることを意味します。

ローダーは、Qwikミドルウェアハンドラー(onRequestonGetonPostなど)の後、Qwikコンポーネントのレンダリング前に実行されます。これにより、ローダーはできるだけ早くデータのフェッチを開始でき、レイテンシを削減できます。

貢献者

このドキュメントの改善に貢献してくださった皆様に感謝いたします!

  • manucorporat
  • mhevery
  • wtlin1228
  • AnthonyPAlicea
  • the-r3aper7
  • hamatoyogi
  • steve8708
  • iamyuu
  • n8sabes
  • mrhoodz
  • mjschwanitz
  • adamdbradley
  • gioboa