Skip to content

Commit

Permalink
perf: Optimise render counts (#849)
Browse files Browse the repository at this point in the history
  • Loading branch information
franky47 authored Jan 8, 2025
1 parent a0a49a9 commit fa63c13
Show file tree
Hide file tree
Showing 30 changed files with 779 additions and 42 deletions.
57 changes: 57 additions & 0 deletions packages/e2e/next/cypress/e2e/shared/render-count.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { testRenderCount } from 'e2e-shared/specs/render-count.cy'

const hooks = ['useQueryState', 'useQueryStates'] as const
const shallows = [true, false] as const
const histories = ['replace', 'push'] as const

for (const hook of hooks) {
for (const shallow of shallows) {
for (const history of histories) {
for (const startTransition of [false, true]) {
for (const delay of shallow === false ? [0, 50] : [0]) {
testRenderCount({
path: `/app/render-count/${hook}/${shallow}/${history}/${startTransition}?delay=${delay}`,
hook,
props: {
shallow,
history,
startTransition,
delay
},
expected: {
mount: 1,
update: shallow === false ? 3 : 2
},
nextJsRouter: 'app'
})
}
}
}
}
}

for (const hook of hooks) {
for (const shallow of shallows) {
for (const history of histories) {
for (const startTransition of [false, true]) {
for (const delay of shallow === false ? [0, 50] : [0]) {
testRenderCount({
path: `/pages/render-count/${hook}/${shallow}/${history}/${startTransition}?delay=${delay}`,
hook,
props: {
shallow,
history,
startTransition,
delay
},
expected: {
mount: 1,
update: 2
},
nextJsRouter: 'pages'
})
}
}
}
}
}
2 changes: 1 addition & 1 deletion packages/e2e/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"start": "NODE_OPTIONS='--enable-source-maps=true' next start --port 3001",
"pretest": "cypress install",
"test": "start-server-and-test start http://localhost:3001${BASE_PATH} cypress:run",
"cypress:open": "cypress open",
"cypress:open": "cypress open --e2e --browser electron",
"cypress:run": "cypress run --headless"
},
"dependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { RenderCount } from 'e2e-shared/specs/render-count'
import {
loadParams,
loadSearchParams
} from 'e2e-shared/specs/render-count.params'
import { setTimeout } from 'node:timers/promises'
import { type SearchParams } from 'nuqs/server'
import { Suspense } from 'react'

export const dynamic = 'force-dynamic'

type PageProps = {
params: Promise<Record<keyof ReturnType<typeof loadParams>, string>>
searchParams: Promise<SearchParams>
}

export default async function Page({
params,
searchParams
}: PageProps & { searchParams: Promise<SearchParams> }) {
const { hook, shallow, history, startTransition } = await loadParams(params)
const { delay } = await loadSearchParams(searchParams)
if (delay) {
await setTimeout(delay)
}
return (
<Suspense>
<RenderCount
hook={hook}
shallow={shallow}
history={history}
startTransition={startTransition}
/>
</Suspense>
)
}

export async function generateStaticParams() {
const hooks = ['useQueryState', 'useQueryStates']
const shallow = [true, false]
const history = ['push', 'replace']
return hooks.flatMap(hook =>
shallow.flatMap(shallow =>
history.map(history => ({ hook, shallow: shallow.toString(), history }))
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { RenderCount } from 'e2e-shared/specs/render-count'
import {
loadParams,
loadSearchParams,
type Params
} from 'e2e-shared/specs/render-count.params'
import { GetServerSideProps } from 'next'
import { setTimeout } from 'node:timers/promises'

export default RenderCount

// We need SSR to get the correct initial render counts
// otherwise with SSG we get at least one extra render for hydration.
export const getServerSideProps: GetServerSideProps<Params> = async ({
params,
query
}) => {
const { delay } = loadSearchParams(query)
if (delay) {
await setTimeout(delay)
}
return {
props: loadParams(params!)
}
}
80 changes: 80 additions & 0 deletions packages/e2e/react-router/v6/cypress/e2e/shared/render-count.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { testRenderCount } from 'e2e-shared/specs/render-count.cy'

const hooks = ['useQueryState', 'useQueryStates'] as const
const shallows = [true, false] as const
const histories = ['replace', 'push'] as const

for (const hook of hooks) {
for (const shallow of shallows) {
for (const history of histories) {
for (const startTransition of [false, true]) {
testRenderCount({
path: `/render-count/${hook}/${shallow}/${history}/${startTransition}/no-loader`,
description: 'no loader',
hook,
props: {
shallow,
history,
startTransition
},
expected: {
mount: 1,
update: 2 + (shallow === false ? (startTransition ? 0 : 1) : 0)
}
})
}
}
}
}

for (const hook of hooks) {
for (const shallow of shallows) {
for (const history of histories) {
for (const startTransition of [false, true]) {
testRenderCount({
path: `/render-count/${hook}/${shallow}/${history}/${startTransition}/sync-loader`,
description: 'sync loader',
hook,
props: {
shallow,
history,
startTransition
},
expected: {
mount: 1,
update: 2 + (shallow === false ? 1 : 0)
}
})
}
}
}
}

for (const hook of hooks) {
for (const shallow of shallows) {
for (const history of histories) {
for (const startTransition of [false, true]) {
for (const delay of shallow === false ? [0, 50] : [0]) {
testRenderCount({
path: `/render-count/${hook}/${shallow}/${history}/${startTransition}/async-loader?delay=${delay}`,
description: 'async loader',
hook,
props: {
shallow,
history,
startTransition,
delay
},
expected: {
mount: 1,
update:
2 +
(shallow === false ? 1 : 0) +
(!startTransition && delay ? 1 : 0)
}
})
}
}
}
}
}
5 changes: 5 additions & 0 deletions packages/e2e/react-router/v6/src/react-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ const router = createBrowserRouter(
<Route path="form/useQueryStates" lazy={load(import('./routes/form.useQueryStates'))} />
<Route path="referential-stability/useQueryState" lazy={load(import('./routes/referential-stability.useQueryState'))} />
<Route path="referential-stability/useQueryStates" lazy={load(import('./routes/referential-stability.useQueryStates'))} />

<Route path="render-count/:hook/:shallow/:history/:startTransition/no-loader" lazy={load(import('./routes/render-count.$hook.$shallow.$history.$startTransition.no-loader'))} />
<Route path="render-count/:hook/:shallow/:history/:startTransition/sync-loader" lazy={load(import('./routes/render-count.$hook.$shallow.$history.$startTransition.sync-loader'))} />
<Route path="render-count/:hook/:shallow/:history/:startTransition/async-loader" lazy={load(import('./routes/render-count.$hook.$shallow.$history.$startTransition.async-loader'))} />

{/* Reproductions */}
<Route path='repro-839' lazy={load(import('./routes/repro-839'))} />
</Route>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { RenderCount } from 'e2e-shared/specs/render-count'
import {
loadParams,
loadSearchParams
} from 'e2e-shared/specs/render-count.params'

import { useParams, type LoaderFunctionArgs } from 'react-router-dom'

const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))

export async function loader({ request }: LoaderFunctionArgs) {
const { delay } = loadSearchParams(request)
if (delay) {
await wait(delay)
}
return null
}

export default function Page() {
const params = loadParams(useParams())
return <RenderCount {...params} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { RenderCount } from 'e2e-shared/specs/render-count'
import { loadParams } from 'e2e-shared/specs/render-count.params'
import { useParams } from 'react-router-dom'

export default function Page() {
const params = loadParams(useParams())
return <RenderCount {...params} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { RenderCount } from 'e2e-shared/specs/render-count'
import { loadParams } from 'e2e-shared/specs/render-count.params'
import { useParams } from 'react-router-dom'

export function loader() {
return null
}

export default function Page() {
const params = loadParams(useParams())
return <RenderCount {...params} />
}
3 changes: 3 additions & 0 deletions packages/e2e/react-router/v7/app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export default [
route('/form/useQueryStates', './routes/form.useQueryStates.tsx'),
route('/referential-stability/useQueryState', './routes/referential-stability.useQueryState.tsx'),
route('/referential-stability/useQueryStates', './routes/referential-stability.useQueryStates.tsx'),
route('/render-count/:hook/:shallow/:history/:startTransition/no-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.no-loader.tsx'),
route('/render-count/:hook/:shallow/:history/:startTransition/sync-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.sync-loader.tsx'),
route('/render-count/:hook/:shallow/:history/:startTransition/async-loader', './routes/render-count.$hook.$shallow.$history.$startTransition.async-loader.tsx'),
// Reproductions
route('/repro-839', './routes/repro-839.tsx'),
])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { RenderCount } from 'e2e-shared/specs/render-count'
import {
loadParams,
loadSearchParams
} from 'e2e-shared/specs/render-count.params'
import { setTimeout } from 'node:timers/promises'
import { useParams, type LoaderFunctionArgs } from 'react-router'

export async function loader({ request }: LoaderFunctionArgs) {
const { delay } = loadSearchParams(request)
if (delay) {
await setTimeout(delay)
}
return null
}

export default function Page() {
const params = loadParams(useParams())
return <RenderCount {...params} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { RenderCount } from 'e2e-shared/specs/render-count'
import { loadParams } from 'e2e-shared/specs/render-count.params'
import { useParams } from 'react-router'

export default function Page() {
const params = loadParams(useParams())
return <RenderCount {...params} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { RenderCount } from 'e2e-shared/specs/render-count'
import { loadParams } from 'e2e-shared/specs/render-count.params'
import { useParams } from 'react-router'

export function loader() {
return null
}

export default function Page() {
const params = loadParams(useParams())
return <RenderCount {...params} />
}
Loading

0 comments on commit fa63c13

Please sign in to comment.