diff --git a/.prettierignore b/.prettierignore index e53f45f2..0d7dcb7d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,4 +3,5 @@ /generated /public +README.md integrations/next/.next \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d3487914..1bdbb206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 2.2.6 + +- `useDynamicRowHeight` should not instantiate `ResizeObserver` when server-rendering + ## 2.2.5 - Use `defaultHeight`/`defaultWidth` prop to server render initial set of rows/cells diff --git a/README.md b/README.md index 4d66fb26..562eae43 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,7 @@ Documentation for this project is available at [react-window.vercel.app](https:/ ### List - Renders data with many rows. - #### Required props @@ -168,12 +166,10 @@ The default value is "div", meaning that List renders an HTMLDivElemen ### Grid - Renders data with many rows and columns. ℹ️ Unlike `List` rows, `Grid` cell sizes must be known ahead of time. Either static sizes or something that can be derived (from the data in `CellProps`) without rendering. - #### Required props diff --git a/integrations/next/app/list-dynamic/components/List.tsx b/integrations/next/app/list-dynamic/components/List.tsx new file mode 100644 index 00000000..41b7e748 --- /dev/null +++ b/integrations/next/app/list-dynamic/components/List.tsx @@ -0,0 +1,22 @@ +"use client"; + +import { List as ListExternal, useDynamicRowHeight } from "react-window"; +import { RowComponent } from "./RowComponent"; + +export function List() { + const rowHeight = useDynamicRowHeight({ + defaultRowHeight: 30 + }); + + return ( + + ); +} diff --git a/integrations/next/app/list-dynamic/components/RowComponent.tsx b/integrations/next/app/list-dynamic/components/RowComponent.tsx new file mode 100644 index 00000000..061bda58 --- /dev/null +++ b/integrations/next/app/list-dynamic/components/RowComponent.tsx @@ -0,0 +1,15 @@ +"use client"; + +import { type RowComponentProps } from "react-window"; + +export function RowComponent({ + ariaAttributes, + index, + style +}: RowComponentProps) { + return ( +
+ Row {index} +
+ ); +} diff --git a/integrations/next/app/list-dynamic/page.tsx b/integrations/next/app/list-dynamic/page.tsx new file mode 100644 index 00000000..c24d59b6 --- /dev/null +++ b/integrations/next/app/list-dynamic/page.tsx @@ -0,0 +1,17 @@ +import { + AnimationFrameRowCellCounter, + EnvironmentMarker, + LayoutShiftDetecter +} from "../../../tests"; +import { List } from "./components/List"; + +export default async function Home() { + return ( +
+ NextJS (server components) + + + +
+ ); +} diff --git a/integrations/next/app/page.tsx b/integrations/next/app/page.tsx index 84f50070..b61f6a49 100644 --- a/integrations/next/app/page.tsx +++ b/integrations/next/app/page.tsx @@ -4,6 +4,7 @@ export default async function Home() { return (
List + List + useDynamicRowHeight Grid
); diff --git a/integrations/vike/pages/index/+Page.tsx b/integrations/vike/pages/index/+Page.tsx index a2ca53b1..c65b9166 100644 --- a/integrations/vike/pages/index/+Page.tsx +++ b/integrations/vike/pages/index/+Page.tsx @@ -2,6 +2,7 @@ export default function Page() { return (
List + List + useDynamicRowHeight Grid
); diff --git a/integrations/vike/pages/list-dynamic/+Page.tsx b/integrations/vike/pages/list-dynamic/+Page.tsx new file mode 100644 index 00000000..64fca9c4 --- /dev/null +++ b/integrations/vike/pages/list-dynamic/+Page.tsx @@ -0,0 +1,30 @@ +import { List, useDynamicRowHeight } from "react-window"; +import { + AnimationFrameRowCellCounter, + EnvironmentMarker, + LayoutShiftDetecter +} from "../../../tests"; +import { RowComponent } from "./RowComponent"; + +export default function Page() { + const rowHeight = useDynamicRowHeight({ + defaultRowHeight: 30 + }); + + return ( +
+ Vike (server rendering) + + + +
+ ); +} diff --git a/integrations/vike/pages/list-dynamic/RowComponent.tsx b/integrations/vike/pages/list-dynamic/RowComponent.tsx new file mode 100644 index 00000000..f872f229 --- /dev/null +++ b/integrations/vike/pages/list-dynamic/RowComponent.tsx @@ -0,0 +1,13 @@ +import { type RowComponentProps } from "react-window"; + +export function RowComponent({ + ariaAttributes, + index, + style +}: RowComponentProps) { + return ( +
+ Row {index} +
+ ); +} diff --git a/lib/components/list/useDynamicRowHeight.ts b/lib/components/list/useDynamicRowHeight.ts index 579d2896..f5d9c654 100644 --- a/lib/components/list/useDynamicRowHeight.ts +++ b/lib/components/list/useDynamicRowHeight.ts @@ -103,22 +103,29 @@ export function useDynamicRowHeight({ } ); - const [resizeObserver] = useState( - () => new ResizeObserver(resizeObserverCallback) - ); + const [resizeObserver] = useState(() => { + if (typeof ResizeObserver !== "undefined") { + return new ResizeObserver(resizeObserverCallback); + } + }); useEffect(() => { - return () => { - resizeObserver.disconnect(); - }; + if (resizeObserver) { + return () => { + resizeObserver.disconnect(); + }; + } }, [resizeObserver]); const observeRowElements = useCallback( (elements: Element[] | NodeListOf) => { - elements.forEach((element) => resizeObserver.observe(element)); - return () => { - elements.forEach((element) => resizeObserver.unobserve(element)); - }; + if (resizeObserver) { + elements.forEach((element) => resizeObserver.observe(element)); + return () => { + elements.forEach((element) => resizeObserver.unobserve(element)); + }; + } + return () => {}; }, [resizeObserver] ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a30973a8..04625a12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -394,6 +394,13 @@ packages: } engines: { node: ">=6.9.0" } + "@babel/code-frame@7.28.6": + resolution: + { + integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q== + } + engines: { node: ">=6.9.0" } + "@babel/compat-data@7.28.0": resolution: { @@ -558,6 +565,13 @@ packages: } engines: { node: ">=6.9.0" } + "@babel/runtime@7.28.4": + resolution: + { + integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== + } + engines: { node: ">=6.9.0" } + "@babel/template@7.27.2": resolution: { @@ -6973,6 +6987,7 @@ packages: integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw== } engines: { node: ">=18" } + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me terser@5.43.1: resolution: @@ -7549,6 +7564,7 @@ packages: integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== } engines: { node: ">=18" } + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@4.0.0: resolution: @@ -7785,6 +7801,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + "@babel/code-frame@7.28.6": + dependencies: + "@babel/helper-validator-identifier": 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + "@babel/compat-data@7.28.0": {} "@babel/core@7.28.0": @@ -7918,6 +7940,8 @@ snapshots: "@babel/runtime@7.27.6": {} + "@babel/runtime@7.28.4": {} + "@babel/template@7.27.2": dependencies: "@babel/code-frame": 7.27.1 @@ -9101,8 +9125,8 @@ snapshots: "@testing-library/dom@10.4.0": dependencies: - "@babel/code-frame": 7.27.1 - "@babel/runtime": 7.27.6 + "@babel/code-frame": 7.28.6 + "@babel/runtime": 7.28.4 "@types/aria-query": 5.0.4 aria-query: 5.3.0 chalk: 4.1.2 @@ -10176,7 +10200,7 @@ snapshots: eslint: 9.30.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.1(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.30.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.30.1(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.30.1(jiti@2.6.1)) @@ -10209,7 +10233,7 @@ snapshots: tinyglobby: 0.2.14 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -10224,7 +10248,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)))(eslint@9.30.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.1(eslint@9.30.1(jiti@2.6.1))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.30.1(jiti@2.6.1)): dependencies: "@rtsao/scc": 1.1.0 array-includes: 3.1.9