Skip to content

Commit 9437fee

Browse files
refactor(router-core,react-router,solid-router): avoid copying arrays just to .reverse() them (#4980)
The pattern of copying an array just to `.reverse()` it and iterate it from the end ends up creating a transient object (a copy of the array) that needs to be garbage collected. For example: ```ts [...array].reverse().find(() => {/*...*/}) ``` or ```ts [...array].reverse().forEach(() => {/*...*/}) ``` Instead we can use a regular `for` loop to iterate it from the end. This will be mostly beneficial in `router-core > router > buildLocation` that gets called pretty often. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Improved head metadata processing in React and Solid routers to be more efficient and readable, reducing work for large route metadata sets. - Simplified route matching logic to consistently pick the last relevant match, improving clarity and maintainability. - **Chores** - Added a utility to locate the last list item meeting a condition, supporting cleaner routing internals. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 72b65cf commit 9437fee

File tree

4 files changed

+33
-18
lines changed

4 files changed

+33
-18
lines changed

packages/react-router/src/HeadContent.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ export const useTags = () => {
1717
const resultMeta: Array<RouterManagedTag> = []
1818
const metaByAttribute: Record<string, true> = {}
1919
let title: RouterManagedTag | undefined
20-
;[...routeMeta].reverse().forEach((metas) => {
21-
;[...metas].reverse().forEach((m) => {
22-
if (!m) return
20+
for (let i = routeMeta.length - 1; i >= 0; i--) {
21+
const metas = routeMeta[i]!
22+
for (let j = metas.length - 1; j >= 0; j--) {
23+
const m = metas[j]
24+
if (!m) continue
2325

2426
if (m.title) {
2527
if (!title) {
@@ -32,7 +34,7 @@ export const useTags = () => {
3234
const attribute = m.name ?? m.property
3335
if (attribute) {
3436
if (metaByAttribute[attribute]) {
35-
return
37+
continue
3638
} else {
3739
metaByAttribute[attribute] = true
3840
}
@@ -45,8 +47,8 @@ export const useTags = () => {
4547
},
4648
})
4749
}
48-
})
49-
})
50+
}
51+
}
5052

5153
if (title) {
5254
resultMeta.push(title)

packages/router-core/src/router.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import invariant from 'tiny-invariant'
88
import {
99
createControlledPromise,
1010
deepEqual,
11+
findLast,
1112
functionalUpdate,
1213
last,
1314
pick,
@@ -1441,13 +1442,11 @@ export class RouterCore<
14411442
undefined,
14421443
).matchedRoutes
14431444

1444-
const matchedFrom = [...allCurrentLocationMatches]
1445-
.reverse()
1446-
.find((d) => {
1447-
return comparePaths(d.fullPath, fromPath)
1448-
})
1445+
const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
1446+
return comparePaths(d.fullPath, fromPath)
1447+
})
14491448

1450-
const matchedCurrent = [...allFromMatches].reverse().find((d) => {
1449+
const matchedCurrent = findLast(allFromMatches, (d) => {
14511450
return comparePaths(d.fullPath, currentLocation.pathname)
14521451
})
14531452

packages/router-core/src/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,3 +483,14 @@ export function isPromise<T>(
483483
typeof (value as Promise<T>).then === 'function',
484484
)
485485
}
486+
487+
export function findLast<T>(
488+
array: ReadonlyArray<T>,
489+
predicate: (item: T) => boolean,
490+
): T | undefined {
491+
for (let i = array.length - 1; i >= 0; i--) {
492+
const item = array[i]!
493+
if (predicate(item)) return item
494+
}
495+
return undefined
496+
}

packages/solid-router/src/HeadContent.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ export const useTags = () => {
1818
const resultMeta: Array<RouterManagedTag> = []
1919
const metaByAttribute: Record<string, true> = {}
2020
let title: RouterManagedTag | undefined
21-
;[...routeMeta()].reverse().forEach((metas) => {
22-
;[...metas].reverse().forEach((m) => {
23-
if (!m) return
21+
const routeMetasArray = routeMeta()
22+
for (let i = routeMetasArray.length - 1; i >= 0; i--) {
23+
const metas = routeMetasArray[i]!
24+
for (let j = metas.length - 1; j >= 0; j--) {
25+
const m = metas[j]
26+
if (!m) continue
2427

2528
if (m.title) {
2629
if (!title) {
@@ -33,7 +36,7 @@ export const useTags = () => {
3336
const attribute = m.name ?? m.property
3437
if (attribute) {
3538
if (metaByAttribute[attribute]) {
36-
return
39+
continue
3740
} else {
3841
metaByAttribute[attribute] = true
3942
}
@@ -46,8 +49,8 @@ export const useTags = () => {
4649
},
4750
})
4851
}
49-
})
50-
})
52+
}
53+
}
5154

5255
if (title) {
5356
resultMeta.push(title)

0 commit comments

Comments
 (0)