Endloser Feed
Ein Feed, der nie endet, muss laden, während der Nutzer zuschaut: früh genug, dass der nächste Beitrag schon da ist, mit etwas zum Anzeigen, solange eine Seite unterwegs ist oder fehlgeschlagen ist, und ohne dass eine Anfrage weiterläuft, wenn der Feed schon weg ist.
Ausprobieren
Jede Version importiert ./api, das für dein Backend steht: fetchPosts(page, signal) liefert eine Seite mit Beiträgen und bricht ab, sobald das Signal abbricht. Ersetze es durch einen Aufruf deiner API und entferne failNextRequest, das nur den Fehlerschalter der Demo steuert.
import { createSignal } from '@reelkit/core';
import { cdnUrl } from '@reelkit/example-data';
// Stands in for your backend. The recipe only relies on the signature of
// `fetchPosts`: a page number in, a page of posts out, cancellable through an
// AbortSignal.
export interface Post {
id: string;
title: string;
image: string;
}
const kPageSize = 6;
const kDelayMs = 800;
const kImages = Array.from(
{ length: 9 },
(_, i) => `samples/images/image-${String(i + 1).padStart(2, '0')}.jpg`,
);
/**
* Demo switch: while on, the next request fails, so the error slide can be
* seen. A signal, so the switch shows when a request has used it up. Only the
* demo on this page uses it: delete it, and the `@reelkit/core` import with
* it, when you replace `fetchPosts` with your API.
*/
export const failNextRequest = createSignal(false);
export function fetchPosts(page: number, signal: AbortSignal): Promise<Post[]> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
if (failNextRequest.value) {
failNextRequest.value = false;
reject(new Error('The feed could not be loaded.'));
return;
}
resolve(
Array.from({ length: kPageSize }, (_, i) => {
const n = page * kPageSize + i;
return {
id: `post-${n}`,
title: `Post ${n + 1}`,
image: cdnUrl(kImages[n % kImages.length]),
};
}),
);
}, kDelayMs);
signal.addEventListener(
'abort',
() => {
clearTimeout(timer);
reject(signal.reason);
},
{ once: true },
);
});
}Die Beispielmedien funktionieren nur auf reelkit.dev
cdnUrl baut Links zu cdn.reelkit.dev, das seine Dateien nur an die Seiten von ReelKit ausliefert. In deiner App laden diese Bilder nicht, also lass image auf deine eigenen Medien zeigen, bevor du diese Datei kopierst.
Wische nach oben oder drücke ↓: Eine neue Seite kommt drei Beiträge vor dem Ende an. Drücke „Fail next request“, um den Fehlerslide und sein Retry zu sehen.
Warum es funktioniert
- Die Anzahl wächst, die Slides nicht.
countist die Zahl der Beiträge plus eins, und es sind immer nur drei Slides im DOM, also kostet ein Feed mit Tausenden Beiträgen so viel wie einer mit zehn. Siehe Virtualisierung. - Der letzte Slide ist der Status. Der zusätzliche Slide hinter den Beiträgen zeigt „Loading…“, solange eine Seite unterwegs ist, und den Fehler mit Retry, wenn sie fehlgeschlagen ist, sodass der Nutzer nie ins Leere wischt. Er ist nur ein weiterer Fall im itemBuilder, der rendert, wofür der Index gerade steht.
- Vorausladen beim Slidewechsel.
afterChangefordert die nächste Seite an, sobald der Nutzer drei Beiträge vor dem Ende ist. Eine fehlgeschlagene Seite wartet auf Retry, sodass Hin- und Herwischen eine fehlschlagende Anfrage nicht wiederholt. Der Callback steht bei den anderen Events für React, Vue und Angular. - Die Keys folgen den Beiträgen.
keyExtractorliefert die ID jedes Beitrags, sodass ein Slide sein Element behält, wenn hinter ihm eine Seite ankommt, statt aus seinem Index neu gebaut zu werden. Es ist ein Prop von React, Vue und Angular gleichermaßen. - Bilder laden vor ihrem Slide. Ein Slide weiter unten ist noch nicht gemountet, denn nur der sichtbare Bereich ist es, wie die Virtualisierung erklärt. Deshalb werden die Bilder jeder neuen Seite angefordert, sobald die Seite ankommt, und kommen aus dem Browser-Cache, wenn ihr Slide gemountet wird.
- Tasten und Mausrad sind absichtlich aus.
enableNavKeyslauscht auf dem ganzen Fenster, undenableWheelhält das Scrollen der Seite fest, solange der Zeiger über dem Feed ist. Beides passt zu einem Feed, der den Bildschirm füllt, und stört eine Seite, die einen enthält, wie diese hier. Deshalb bleiben sie hier aus, und zwei Buttons rufen stattdessenprev()undnext()der Reel-API auf. Wischen funktioniert in beiden Fällen. - Nichts überlebt den Feed. Jede Anfrage trägt einen
AbortController, der beim Unmount des Feeds abgebrochen wird, sodass eine späte Antwort nie in einen Feed schreibt, den es nicht mehr gibt.
Nächste Schritte
- Vom Raster zum Player - den Vollbild-Player aus einem Raster von Vorschaubildern öffnen und dorthin zurückkehren, wo du warst
- Framework-Guide - jedes Reel-Prop und jedes Muster hinter diesem RezeptFramework-Guide - jedes Reel-Prop und jedes Muster hinter diesem RezeptFramework-Guide - jedes Reel-Prop und jedes Muster hinter diesem Rezept