Skip to content

Commit

Permalink
Merge pull request #79 from inokawa/ssr-count
Browse files Browse the repository at this point in the history
Add initialItemCount prop for SSR
  • Loading branch information
inokawa authored May 11, 2023
2 parents b233833 + 54b1d69 commit 2b564e1
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 25 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ WIP
| Reverse scroll |||||||
| Infinite scroll ||| 🟠 (needs [InfiniteLoader](https://github.com/bvaughn/react-virtualized/blob/master/docs/InfiniteLoader.md)) | 🟠 (needs [react-window-infinite-loader](https://github.com/bvaughn/react-window-infinite-loader)) |||
| RTL |||||||
| SSR support |||||||
| Display exceeding browser's max element size limit |||||||

- ✅ - Built-in supported
Expand Down
14 changes: 10 additions & 4 deletions src/core/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
hasUnmeasuredItemsInRange,
} from "./cache";
import type { Writeable } from "./types";
import { max } from "./utils";

export type ScrollJump = Readonly<[index: number, sizeDiff: number][]>;
export type ItemResize = [index: number, size: number];
Expand Down Expand Up @@ -50,14 +51,19 @@ export const createVirtualStore = (
itemCount: number,
itemSize: number,
isHorizontal: boolean,
isRtl: boolean
isRtl: boolean,
initialItemCount: number = 0
): VirtualStore => {
let viewportWidth = 0;
let viewportHeight = 0;
let viewportWidth = isHorizontal
? itemSize * max(initialItemCount - 1, 0)
: 0;
let viewportHeight = !isHorizontal
? itemSize * max(initialItemCount - 1, 0)
: 0;
let scrollOffset = 0;
let jump: ScrollJump = [];
let cache = resetCache(itemCount, itemSize);
let _prevRange: ItemsRange = [0, 0];
let _prevRange: ItemsRange = [0, initialItemCount];
let _scrollToQueue: [() => void, () => void] | undefined;

const subscribers = new Set<() => void>();
Expand Down
97 changes: 79 additions & 18 deletions src/react/VList.ssr.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,90 @@
import { it, describe, expect } from "@jest/globals";
import { renderToString, renderToStaticMarkup } from "react-dom/server";
import { VList } from "./VList";
import { JSDOM } from "jsdom";

const LIST_ID = "list-id";

describe("SSR", () => {
it("should render 10 items with renderToString", () => {
it("should render items with renderToString and vertical", () => {
const COUNT = 10;
const OVERSCAN = 4;
const html = renderToString(
<VList id={LIST_ID} initialItemCount={COUNT} overscan={OVERSCAN}>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i}>{i}</div>
))}
</VList>
);
expect(html).toMatchSnapshot();

expect(
new JSDOM(html).window.document.getElementById(LIST_ID)!.children[0]!
.childElementCount
).toEqual(COUNT + OVERSCAN);
});

it("should render items with renderToStaticMarkup and vertical", () => {
const COUNT = 10;
const OVERSCAN = 4;
const html = renderToStaticMarkup(
<VList id={LIST_ID} initialItemCount={COUNT} overscan={OVERSCAN}>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i}>{i}</div>
))}
</VList>
);
expect(html).toMatchSnapshot();

expect(
renderToString(
<VList overscan={10}>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i}>{i}</div>
))}
</VList>
)
).toMatchSnapshot();
new JSDOM(html).window.document.getElementById(LIST_ID)!.children[0]!
.childElementCount
).toEqual(COUNT + OVERSCAN);
});

it("should render 10 items with renderToStaticMarkup", () => {
it("should render items with renderToString and horizontal", () => {
const COUNT = 10;
const OVERSCAN = 4;
const html = renderToString(
<VList
id={LIST_ID}
initialItemCount={COUNT}
overscan={OVERSCAN}
horizontal
>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i}>{i}</div>
))}
</VList>
);
expect(html).toMatchSnapshot();

expect(
new JSDOM(html).window.document.getElementById(LIST_ID)!.children[0]!
.childElementCount
).toEqual(COUNT + OVERSCAN);
});

it("should render items with renderToStaticMarkup and horizontal", () => {
const COUNT = 10;
const OVERSCAN = 4;
const html = renderToStaticMarkup(
<VList
id={LIST_ID}
initialItemCount={COUNT}
overscan={OVERSCAN}
horizontal
>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i}>{i}</div>
))}
</VList>
);
expect(html).toMatchSnapshot();

expect(
renderToStaticMarkup(
<VList overscan={10}>
{Array.from({ length: 1000 }).map((_, i) => (
<div key={i}>{i}</div>
))}
</VList>
)
).toMatchSnapshot();
new JSDOM(html).window.document.getElementById(LIST_ID)!.children[0]!
.childElementCount
).toEqual(COUNT + OVERSCAN);
});
});
13 changes: 12 additions & 1 deletion src/react/VList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ export interface VListProps extends WindowComponentAttributes {
* @defaultValue 4
*/
overscan?: number;
/**
* If set, the specified amount of items will be mounted in the initial rendering regardless of the container size. This prop is mostly for SSR.
*/
initialItemCount?: number;
/**
* If true, rendered as a horizontally scrollable list. Otherwise rendered as a vertically scrollable list.
*/
Expand Down Expand Up @@ -304,6 +308,7 @@ export const VList = forwardRef<VListHandle, VListProps>(
children,
itemSize: itemSizeProp = 40,
overscan = 4,
initialItemCount,
horizontal: horizontalProp,
rtl: rtlProp,
element = DefaultWindow,
Expand All @@ -330,7 +335,13 @@ export const VList = forwardRef<VListHandle, VListProps>(

// https://github.com/facebook/react/issues/25191#issuecomment-1237456448
const store = useStatic(() =>
createVirtualStore(count, itemSizeProp, !!horizontalProp, !!rtlProp)
createVirtualStore(
count,
itemSizeProp,
!!horizontalProp,
!!rtlProp,
initialItemCount
)
);
// The elements length and cached items length are different just after element is added/removed.
store._updateCacheLength(count);
Expand Down
8 changes: 6 additions & 2 deletions src/react/__snapshots__/VList.ssr.spec.tsx.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`SSR should render 10 items with renderToStaticMarkup 1`] = `"<div style="overflow:hidden auto;contain:strict;width:100%;height:100%;padding:0;margin:0"><div style="position:relative;visibility:hidden;width:100%;height:40000px;pointer-events:auto"><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:0;visibility:hidden"><div>0</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:40px;visibility:hidden"><div>1</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:80px;visibility:hidden"><div>2</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:120px;visibility:hidden"><div>3</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:160px;visibility:hidden"><div>4</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:200px;visibility:hidden"><div>5</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:240px;visibility:hidden"><div>6</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:280px;visibility:hidden"><div>7</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:320px;visibility:hidden"><div>8</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:360px;visibility:hidden"><div>9</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:400px;visibility:hidden"><div>10</div></div></div></div>"`;
exports[`SSR should render items with renderToStaticMarkup and horizontal 1`] = `"<div id="list-id" style="overflow:auto hidden;contain:strict;width:100%;height:100%;padding:0;margin:0"><div style="position:relative;visibility:hidden;width:40000px;height:100%;pointer-events:auto"><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:0;visibility:hidden;display:flex"><div>0</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:40px;visibility:hidden;display:flex"><div>1</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:80px;visibility:hidden;display:flex"><div>2</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:120px;visibility:hidden;display:flex"><div>3</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:160px;visibility:hidden;display:flex"><div>4</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:200px;visibility:hidden;display:flex"><div>5</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:240px;visibility:hidden;display:flex"><div>6</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:280px;visibility:hidden;display:flex"><div>7</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:320px;visibility:hidden;display:flex"><div>8</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:360px;visibility:hidden;display:flex"><div>9</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:400px;visibility:hidden;display:flex"><div>10</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:440px;visibility:hidden;display:flex"><div>11</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:480px;visibility:hidden;display:flex"><div>12</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:520px;visibility:hidden;display:flex"><div>13</div></div></div></div>"`;

exports[`SSR should render 10 items with renderToString 1`] = `"<div style="overflow:hidden auto;contain:strict;width:100%;height:100%;padding:0;margin:0"><div style="position:relative;visibility:hidden;width:100%;height:40000px;pointer-events:auto"><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:0;visibility:hidden"><div>0</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:40px;visibility:hidden"><div>1</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:80px;visibility:hidden"><div>2</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:120px;visibility:hidden"><div>3</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:160px;visibility:hidden"><div>4</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:200px;visibility:hidden"><div>5</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:240px;visibility:hidden"><div>6</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:280px;visibility:hidden"><div>7</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:320px;visibility:hidden"><div>8</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:360px;visibility:hidden"><div>9</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:400px;visibility:hidden"><div>10</div></div></div></div>"`;
exports[`SSR should render items with renderToStaticMarkup and vertical 1`] = `"<div id="list-id" style="overflow:hidden auto;contain:strict;width:100%;height:100%;padding:0;margin:0"><div style="position:relative;visibility:hidden;width:100%;height:40000px;pointer-events:auto"><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:0;visibility:hidden"><div>0</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:40px;visibility:hidden"><div>1</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:80px;visibility:hidden"><div>2</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:120px;visibility:hidden"><div>3</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:160px;visibility:hidden"><div>4</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:200px;visibility:hidden"><div>5</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:240px;visibility:hidden"><div>6</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:280px;visibility:hidden"><div>7</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:320px;visibility:hidden"><div>8</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:360px;visibility:hidden"><div>9</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:400px;visibility:hidden"><div>10</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:440px;visibility:hidden"><div>11</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:480px;visibility:hidden"><div>12</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:520px;visibility:hidden"><div>13</div></div></div></div>"`;

exports[`SSR should render items with renderToString and horizontal 1`] = `"<div id="list-id" style="overflow:auto hidden;contain:strict;width:100%;height:100%;padding:0;margin:0"><div style="position:relative;visibility:hidden;width:40000px;height:100%;pointer-events:auto"><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:0;visibility:hidden;display:flex"><div>0</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:40px;visibility:hidden;display:flex"><div>1</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:80px;visibility:hidden;display:flex"><div>2</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:120px;visibility:hidden;display:flex"><div>3</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:160px;visibility:hidden;display:flex"><div>4</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:200px;visibility:hidden;display:flex"><div>5</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:240px;visibility:hidden;display:flex"><div>6</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:280px;visibility:hidden;display:flex"><div>7</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:320px;visibility:hidden;display:flex"><div>8</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:360px;visibility:hidden;display:flex"><div>9</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:400px;visibility:hidden;display:flex"><div>10</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:440px;visibility:hidden;display:flex"><div>11</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:480px;visibility:hidden;display:flex"><div>12</div></div><div style="margin:0;padding:0;position:absolute;height:100%;top:0;left:520px;visibility:hidden;display:flex"><div>13</div></div></div></div>"`;

exports[`SSR should render items with renderToString and vertical 1`] = `"<div id="list-id" style="overflow:hidden auto;contain:strict;width:100%;height:100%;padding:0;margin:0"><div style="position:relative;visibility:hidden;width:100%;height:40000px;pointer-events:auto"><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:0;visibility:hidden"><div>0</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:40px;visibility:hidden"><div>1</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:80px;visibility:hidden"><div>2</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:120px;visibility:hidden"><div>3</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:160px;visibility:hidden"><div>4</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:200px;visibility:hidden"><div>5</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:240px;visibility:hidden"><div>6</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:280px;visibility:hidden"><div>7</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:320px;visibility:hidden"><div>8</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:360px;visibility:hidden"><div>9</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:400px;visibility:hidden"><div>10</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:440px;visibility:hidden"><div>11</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:480px;visibility:hidden"><div>12</div></div><div style="margin:0;padding:0;position:absolute;width:100%;left:0;top:520px;visibility:hidden"><div>13</div></div></div></div>"`;
Loading

0 comments on commit 2b564e1

Please sign in to comment.