• בלוג
  • ניסוי Next - טעינת מידע אחרי עליית העמוד עם use

ניסוי Next - טעינת מידע אחרי עליית העמוד עם use

01/03/2025

קשה מאוד להבין את ריאקט היום מחוץ לקונטקסט של next. כתבתי כאן על הסירבול בפונקציה use בגלל הצורך להעביר את ה Promise מבחוץ והבעיה ביצירת Promise מתוך קומפוננטות. עכשיו בואו נראה את הסיפור במשקפיים הנכונות של next ונראה למה בשימוש מלא של הפיצ'ר אין שום בעיה.

1. הבעיה עם קומפוננטות אסינכרוניות

נתבונן בקומפוננטה הבאה:

import { slowlyGetCwd } from "../actions/demo"

export default async function Main() {
  const cwd = await slowlyGetCwd();
  return (
    <div>
      <p>Page content</p>
      <div>Server runs from: <pre>{cwd}</pre></div>
    </div>
  )
}

ובפונקציית צד השרת שמתאימה לה:

'use server';

import {setTimeout} from 'node:timers/promises';

export async function slowlyGetCwd() {
  await setTimeout(3000);
  return process.cwd();
}

מה שקורה כשהעמוד עולה השרת מפעיל את הפונקציה, מחכה 3 שניות ורק אז מסיים לייצר את ה HTML ושולח אותו לדפדפן. ב-3 שניות האלה הגולש פשוט מחכה לתשובה שרת ולא רואה כלום על העמוד. לפעמים זאת התנהגות הגיונית, אולי באמת אנחנו טוענים מידע מאוד בסיסי מבסיס הנתונים ואין מה להראות עד שהמידע הזה מגיע, אבל לפעמים זו המתנה מיותרת - אולי אני מחכה לטעון תמונת פרופיל של המשתמש מאיזה אתר צד שלישי ורק בגלל ההמתנה לתמונה הזאת משתמש צריך לחכות ולא לקבל את המידע שכן יש לי מוכן בשבילו.

2. איך use פותר את הבעיה

פונקציית use החדשה בריאקט 19 מאפשרת גם גישה ל Context API וגם המתנה ל Promises. אותנו מעניין לנסות את השימוש השני בה: אם פונקציית use מקבלת Promise אז הקומפוננטה לא תתרנדר, יוצג במקומה "תחליף" מתוך בלוק ה Suspense הקרוב ביותר, ורק כשה Promise תהיה מוכנה אז הקומפוננטה תופיע.

ב Next אנחנו יכולים להעביר Promise בתור Prop מקומפוננטת צד שרת לקומפוננטת צד לקוח, ולכן נוכל להשתמש במנגנון כדי לשלוח מידע לדפדפן אחרי שהעמוד נטען. זה יעבוד כך:

  1. קומפוננטת צד שרת מפעילה פעולה אסינכרונית שלוקחת זמן.

  2. הקומפוננטה מעבירה את ה Promise לסיום התהליך בתור Prop לקומפוננטת צד לקוח.

  3. קומפוננטת צד לקוח מפעילה use על ה Promise.

  4. כשה Promise מסתיים בצד השרת, השרת שולח לדפדפן את המשך העמוד ב Streaming. זה גורם ל Promise בצד הלקוח להסתיים וכך ריאקט יכול לרנדר את קומפוננטת צד הלקוח עם המידע שהגיע מהשרת.

3. דוגמה: קבלת מידע אחרי שהעמוד נטען

בואו נחליף את קומפוננטת Main שכתבנו בקומפוננטה עם use. תחילה גירסת ה client component של Main:

'use client';
import {use} from 'react';

export default function Main({
  cwdPromise
}: {
  cwdPromise: Promise<string>,
}) {
  const cwd = use(cwdPromise)
  return (
    <div>
      <p>Page content</p>
      <div>Server runs from: <pre>{cwd}</pre></div>
    </div>
  )
}

ועדכון page.tsx כדי לייצר את ה Promise, להעביר אותו כ prop ולהגדיר את התוכן שיוצג בזמן הטעינה:

import Head from "./components/head"
import Main from "./components/main"
import { slowlyGetCwd } from "./actions/demo"
import { Suspense } from "react";

export default function Home() {
  const getCwd = slowlyGetCwd();

  return (
    <div>
      <Head />
      <Suspense fallback={<p>Loading Main</p>}>
        <Main cwdPromise={getCwd} />
      </Suspense>
    </div>
  )
}

התוצאה: העמוד נטען מיד אבל במקום Main מוצג התוכן Loading Main. אחרי 3 שניות תוכן ה Fallback מוחלף בתוכן האמיתי של Main עם התוצאה של הפונקציה האיטית בצד השרת.