@tanstack/react-router 1.31.21 → 1.31.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/utils.js CHANGED
@@ -152,6 +152,16 @@ function removeLayoutSegments(routePath) {
152
152
  const newSegments = segments.filter((segment) => !segment.startsWith("_"));
153
153
  return newSegments.join("/");
154
154
  }
155
+ function usePrevious(value) {
156
+ const ref = React.useRef(value);
157
+ if (ref.current !== value) {
158
+ const prevValue = ref.current;
159
+ ref.current = value;
160
+ return prevValue;
161
+ } else {
162
+ return ref.current;
163
+ }
164
+ }
155
165
  export {
156
166
  createControlledPromise,
157
167
  deepEqual,
@@ -167,6 +177,7 @@ export {
167
177
  replaceEqualDeep,
168
178
  shallow,
169
179
  useLayoutEffect,
180
+ usePrevious,
170
181
  useStableCallback
171
182
  };
172
183
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import * as React from 'react'\n\nexport type NoInfer<T> = [T][T extends any ? 0 : never]\nexport type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue\n ? TYesResult\n : TNoResult\nexport type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<\n TValue,\n TKey\n> &\n Required<Pick<TValue, TKey>>\n\nexport type PickRequired<T> = {\n [K in keyof T as undefined extends T[K] ? never : K]: T[K]\n}\n\n// from https://stackoverflow.com/a/76458160\nexport type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never\n\n// export type Expand<T> = T\nexport type Expand<T> = T extends object\n ? T extends infer O\n ? O extends Function\n ? O\n : { [K in keyof O]: O[K] }\n : never\n : T\n\nexport type UnionToIntersection<T> = (\n T extends any ? (k: T) => void : never\n) extends (k: infer I) => any\n ? I\n : never\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n\nexport type MakeDifferenceOptional<TLeft, TRight> = Omit<\n TRight,\n keyof TLeft\n> & {\n [K in keyof TLeft & keyof TRight]?: TRight[K]\n}\n\n// from https://stackoverflow.com/a/53955431\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport type IsUnion<T, U extends T = T> = (\n T extends any ? (U extends T ? false : true) : never\n) extends false\n ? false\n : true\n\nexport type Assign<TLeft, TRight> = keyof TLeft extends never\n ? TRight\n : keyof TRight extends never\n ? TLeft\n : Omit<TLeft, keyof TRight> & TRight\n\nexport type Timeout = ReturnType<typeof setTimeout>\n\nexport type Updater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev?: TPrevious) => TResult)\n\nexport type NonNullableUpdater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev: TPrevious) => TResult)\n\n// from https://github.com/type-challenges/type-challenges/issues/737\ntype LastInUnion<T> =\n UnionToIntersection<T extends unknown ? (x: T) => 0 : never> extends (\n x: infer L,\n ) => 0\n ? L\n : never\nexport type UnionToTuple<T, TLast = LastInUnion<T>> = [T] extends [never]\n ? []\n : [...UnionToTuple<Exclude<T, TLast>>, TLast]\n\n//\n\nexport function last<T>(arr: Array<T>) {\n return arr[arr.length - 1]\n}\n\nfunction isFunction(d: any): d is Function {\n return typeof d === 'function'\n}\n\nexport function functionalUpdate<TResult>(\n updater: Updater<TResult> | NonNullableUpdater<TResult>,\n previous: TResult,\n): TResult {\n if (isFunction(updater)) {\n return updater(previous)\n }\n\n return updater\n}\n\nexport function pick<TValue, TKey extends keyof TValue>(\n parent: TValue,\n keys: Array<TKey>,\n): Pick<TValue, TKey> {\n return keys.reduce((obj: any, key: TKey) => {\n obj[key] = parent[key]\n return obj\n }, {} as any)\n}\n\n/**\n * This function returns `prev` if `_next` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between immutable JSON values for example.\n * Do not use this with signals\n */\nexport function replaceEqualDeep<T>(prev: any, _next: T): T {\n if (prev === _next) {\n return prev\n }\n\n const next = _next as any\n\n const array = isPlainArray(prev) && isPlainArray(next)\n\n if (array || (isPlainObject(prev) && isPlainObject(next))) {\n const prevItems = array ? prev : Object.keys(prev)\n const prevSize = prevItems.length\n const nextItems = array ? next : Object.keys(next)\n const nextSize = nextItems.length\n const copy: any = array ? [] : {}\n\n let equalItems = 0\n\n for (let i = 0; i < nextSize; i++) {\n const key = array ? i : nextItems[i]\n if (\n ((!array && prevItems.includes(key)) || array) &&\n prev[key] === undefined &&\n next[key] === undefined\n ) {\n copy[key] = undefined\n equalItems++\n } else {\n copy[key] = replaceEqualDeep(prev[key], next[key])\n if (copy[key] === prev[key] && prev[key] !== undefined) {\n equalItems++\n }\n }\n }\n\n return prevSize === nextSize && equalItems === prevSize ? prev : copy\n }\n\n return next\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any) {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has modified constructor\n const ctor = o.constructor\n if (typeof ctor === 'undefined') {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any) {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\nexport function isPlainArray(value: unknown) {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\nexport function deepEqual(a: any, b: any, partial: boolean = false): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const aKeys = Object.keys(a)\n const bKeys = Object.keys(b)\n\n if (!partial && aKeys.length !== bKeys.length) {\n return false\n }\n\n return !bKeys.some(\n (key) => !(key in a) || !deepEqual(a[key], b[key], partial),\n )\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n return !a.some((item, index) => !deepEqual(item, b[index], partial))\n }\n\n return false\n}\n\nexport function useStableCallback<T extends (...args: Array<any>) => any>(\n fn: T,\n): T {\n const fnRef = React.useRef(fn)\n fnRef.current = fn\n\n const ref = React.useRef((...args: Array<any>) => fnRef.current(...args))\n return ref.current as T\n}\n\nexport function shallow<T>(objA: T, objB: T) {\n if (Object.is(objA, objB)) {\n return true\n }\n\n if (\n typeof objA !== 'object' ||\n objA === null ||\n typeof objB !== 'object' ||\n objB === null\n ) {\n return false\n }\n\n const keysA = Object.keys(objA)\n if (keysA.length !== Object.keys(objB).length) {\n return false\n }\n\n for (const item of keysA) {\n if (\n !Object.prototype.hasOwnProperty.call(objB, item) ||\n !Object.is(objA[item as keyof T], objB[item as keyof T])\n ) {\n return false\n }\n }\n return true\n}\n\nexport type StringLiteral<T> = T extends string\n ? string extends T\n ? string\n : T\n : never\n\nexport type StrictOrFrom<TFrom, TReturnIntersection extends boolean = false> =\n | {\n from: StringLiteral<TFrom> | TFrom\n strict?: true\n }\n | {\n from?: never\n strict: false\n experimental_returnIntersection?: TReturnIntersection\n }\n\nexport const useLayoutEffect =\n typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect\n\n/**\n *\n * @deprecated use `jsesc` instead\n */\nexport function escapeJSON(jsonString: string) {\n return jsonString\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n .replace(/'/g, \"\\\\'\") // Escape single quotes\n .replace(/\"/g, '\\\\\"') // Escape double quotes\n}\n\nexport function removeTrailingSlash(value: string): string {\n if (value.endsWith('/') && value !== '/') {\n return value.slice(0, -1)\n }\n return value\n}\n\n// intended to only compare path name\n// see the usage in the isActive under useLinkProps\n// /sample/path1 = /sample/path1/\n// /sample/path1/some <> /sample/path1\nexport function exactPathTest(pathName1: string, pathName2: string): boolean {\n return removeTrailingSlash(pathName1) === removeTrailingSlash(pathName2)\n}\n\nexport type ControlledPromise<T> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value: any) => void\n status: 'pending' | 'resolved' | 'rejected'\n}\n\nexport function createControlledPromise<T>(onResolve?: () => void) {\n let resolveLoadPromise!: () => void\n let rejectLoadPromise!: (value: any) => void\n\n const controlledPromise = new Promise<void>((resolve, reject) => {\n resolveLoadPromise = resolve\n rejectLoadPromise = reject\n }) as ControlledPromise<T>\n\n controlledPromise.status = 'pending'\n\n controlledPromise.resolve = () => {\n controlledPromise.status = 'resolved'\n resolveLoadPromise()\n onResolve?.()\n }\n\n controlledPromise.reject = (e) => {\n controlledPromise.status = 'rejected'\n rejectLoadPromise(e)\n }\n\n return controlledPromise\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sources":["../../src/utils.ts"],"sourcesContent":["import * as React from 'react'\n\nexport type NoInfer<T> = [T][T extends any ? 0 : never]\nexport type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue\n ? TYesResult\n : TNoResult\nexport type PickAsRequired<TValue, TKey extends keyof TValue> = Omit<\n TValue,\n TKey\n> &\n Required<Pick<TValue, TKey>>\n\nexport type PickRequired<T> = {\n [K in keyof T as undefined extends T[K] ? never : K]: T[K]\n}\n\n// from https://stackoverflow.com/a/76458160\nexport type WithoutEmpty<T> = T extends any ? ({} extends T ? never : T) : never\n\n// export type Expand<T> = T\nexport type Expand<T> = T extends object\n ? T extends infer O\n ? O extends Function\n ? O\n : { [K in keyof O]: O[K] }\n : never\n : T\n\nexport type UnionToIntersection<T> = (\n T extends any ? (k: T) => void : never\n) extends (k: infer I) => any\n ? I\n : never\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n\nexport type MakeDifferenceOptional<TLeft, TRight> = Omit<\n TRight,\n keyof TLeft\n> & {\n [K in keyof TLeft & keyof TRight]?: TRight[K]\n}\n\n// from https://stackoverflow.com/a/53955431\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport type IsUnion<T, U extends T = T> = (\n T extends any ? (U extends T ? false : true) : never\n) extends false\n ? false\n : true\n\nexport type Assign<TLeft, TRight> = keyof TLeft extends never\n ? TRight\n : keyof TRight extends never\n ? TLeft\n : Omit<TLeft, keyof TRight> & TRight\n\nexport type Timeout = ReturnType<typeof setTimeout>\n\nexport type Updater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev?: TPrevious) => TResult)\n\nexport type NonNullableUpdater<TPrevious, TResult = TPrevious> =\n | TResult\n | ((prev: TPrevious) => TResult)\n\n// from https://github.com/type-challenges/type-challenges/issues/737\ntype LastInUnion<T> =\n UnionToIntersection<T extends unknown ? (x: T) => 0 : never> extends (\n x: infer L,\n ) => 0\n ? L\n : never\nexport type UnionToTuple<T, TLast = LastInUnion<T>> = [T] extends [never]\n ? []\n : [...UnionToTuple<Exclude<T, TLast>>, TLast]\n\n//\n\nexport function last<T>(arr: Array<T>) {\n return arr[arr.length - 1]\n}\n\nfunction isFunction(d: any): d is Function {\n return typeof d === 'function'\n}\n\nexport function functionalUpdate<TResult>(\n updater: Updater<TResult> | NonNullableUpdater<TResult>,\n previous: TResult,\n): TResult {\n if (isFunction(updater)) {\n return updater(previous)\n }\n\n return updater\n}\n\nexport function pick<TValue, TKey extends keyof TValue>(\n parent: TValue,\n keys: Array<TKey>,\n): Pick<TValue, TKey> {\n return keys.reduce((obj: any, key: TKey) => {\n obj[key] = parent[key]\n return obj\n }, {} as any)\n}\n\n/**\n * This function returns `prev` if `_next` is deeply equal.\n * If not, it will replace any deeply equal children of `b` with those of `a`.\n * This can be used for structural sharing between immutable JSON values for example.\n * Do not use this with signals\n */\nexport function replaceEqualDeep<T>(prev: any, _next: T): T {\n if (prev === _next) {\n return prev\n }\n\n const next = _next as any\n\n const array = isPlainArray(prev) && isPlainArray(next)\n\n if (array || (isPlainObject(prev) && isPlainObject(next))) {\n const prevItems = array ? prev : Object.keys(prev)\n const prevSize = prevItems.length\n const nextItems = array ? next : Object.keys(next)\n const nextSize = nextItems.length\n const copy: any = array ? [] : {}\n\n let equalItems = 0\n\n for (let i = 0; i < nextSize; i++) {\n const key = array ? i : nextItems[i]\n if (\n ((!array && prevItems.includes(key)) || array) &&\n prev[key] === undefined &&\n next[key] === undefined\n ) {\n copy[key] = undefined\n equalItems++\n } else {\n copy[key] = replaceEqualDeep(prev[key], next[key])\n if (copy[key] === prev[key] && prev[key] !== undefined) {\n equalItems++\n }\n }\n }\n\n return prevSize === nextSize && equalItems === prevSize ? prev : copy\n }\n\n return next\n}\n\n// Copied from: https://github.com/jonschlinkert/is-plain-object\nexport function isPlainObject(o: any) {\n if (!hasObjectPrototype(o)) {\n return false\n }\n\n // If has modified constructor\n const ctor = o.constructor\n if (typeof ctor === 'undefined') {\n return true\n }\n\n // If has modified prototype\n const prot = ctor.prototype\n if (!hasObjectPrototype(prot)) {\n return false\n }\n\n // If constructor does not have an Object-specific method\n if (!prot.hasOwnProperty('isPrototypeOf')) {\n return false\n }\n\n // Most likely a plain Object\n return true\n}\n\nfunction hasObjectPrototype(o: any) {\n return Object.prototype.toString.call(o) === '[object Object]'\n}\n\nexport function isPlainArray(value: unknown) {\n return Array.isArray(value) && value.length === Object.keys(value).length\n}\n\nexport function deepEqual(a: any, b: any, partial: boolean = false): boolean {\n if (a === b) {\n return true\n }\n\n if (typeof a !== typeof b) {\n return false\n }\n\n if (isPlainObject(a) && isPlainObject(b)) {\n const aKeys = Object.keys(a)\n const bKeys = Object.keys(b)\n\n if (!partial && aKeys.length !== bKeys.length) {\n return false\n }\n\n return !bKeys.some(\n (key) => !(key in a) || !deepEqual(a[key], b[key], partial),\n )\n }\n\n if (Array.isArray(a) && Array.isArray(b)) {\n return !a.some((item, index) => !deepEqual(item, b[index], partial))\n }\n\n return false\n}\n\nexport function useStableCallback<T extends (...args: Array<any>) => any>(\n fn: T,\n): T {\n const fnRef = React.useRef(fn)\n fnRef.current = fn\n\n const ref = React.useRef((...args: Array<any>) => fnRef.current(...args))\n return ref.current as T\n}\n\nexport function shallow<T>(objA: T, objB: T) {\n if (Object.is(objA, objB)) {\n return true\n }\n\n if (\n typeof objA !== 'object' ||\n objA === null ||\n typeof objB !== 'object' ||\n objB === null\n ) {\n return false\n }\n\n const keysA = Object.keys(objA)\n if (keysA.length !== Object.keys(objB).length) {\n return false\n }\n\n for (const item of keysA) {\n if (\n !Object.prototype.hasOwnProperty.call(objB, item) ||\n !Object.is(objA[item as keyof T], objB[item as keyof T])\n ) {\n return false\n }\n }\n return true\n}\n\nexport type StringLiteral<T> = T extends string\n ? string extends T\n ? string\n : T\n : never\n\nexport type StrictOrFrom<TFrom, TReturnIntersection extends boolean = false> =\n | {\n from: StringLiteral<TFrom> | TFrom\n strict?: true\n }\n | {\n from?: never\n strict: false\n experimental_returnIntersection?: TReturnIntersection\n }\n\nexport const useLayoutEffect =\n typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect\n\n/**\n *\n * @deprecated use `jsesc` instead\n */\nexport function escapeJSON(jsonString: string) {\n return jsonString\n .replace(/\\\\/g, '\\\\\\\\') // Escape backslashes\n .replace(/'/g, \"\\\\'\") // Escape single quotes\n .replace(/\"/g, '\\\\\"') // Escape double quotes\n}\n\nexport function removeTrailingSlash(value: string): string {\n if (value.endsWith('/') && value !== '/') {\n return value.slice(0, -1)\n }\n return value\n}\n\n// intended to only compare path name\n// see the usage in the isActive under useLinkProps\n// /sample/path1 = /sample/path1/\n// /sample/path1/some <> /sample/path1\nexport function exactPathTest(pathName1: string, pathName2: string): boolean {\n return removeTrailingSlash(pathName1) === removeTrailingSlash(pathName2)\n}\n\nexport type ControlledPromise<T> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value: any) => void\n status: 'pending' | 'resolved' | 'rejected'\n}\n\nexport function createControlledPromise<T>(onResolve?: () => void) {\n let resolveLoadPromise!: () => void\n let rejectLoadPromise!: (value: any) => void\n\n const controlledPromise = new Promise<void>((resolve, reject) => {\n resolveLoadPromise = resolve\n rejectLoadPromise = reject\n }) as ControlledPromise<T>\n\n controlledPromise.status = 'pending'\n\n controlledPromise.resolve = () => {\n controlledPromise.status = 'resolved'\n resolveLoadPromise()\n onResolve?.()\n }\n\n controlledPromise.reject = (e) => {\n controlledPromise.status = 'rejected'\n rejectLoadPromise(e)\n }\n\n return controlledPromise\n}\n\n/**\n * Removes all segments from a given path that start with an underscore ('_').\n *\n * @param {string} routePath - The path from which to remove segments. Defaults to '/'.\n * @returns {string} The path with all underscore-prefixed segments removed.\n * @example\n * removeLayoutSegments('/workspace/_auth/foo') // '/workspace/foo'\n */\nexport function removeLayoutSegments(routePath: string): string {\n const segments = routePath.split('/')\n const newSegments = segments.filter((segment) => !segment.startsWith('_'))\n return newSegments.join('/')\n}\n\nexport function usePrevious<T>(value: T): T {\n const ref = React.useRef<T>(value)\n\n if (ref.current !== value) {\n const prevValue = ref.current\n ref.current = value\n return prevValue\n } else {\n return ref.current\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.31.21",
3
+ "version": "1.31.23",
4
4
  "description": "",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/Matches.tsx CHANGED
@@ -8,6 +8,7 @@ import { createControlledPromise, pick } from './utils'
8
8
  import { CatchNotFound, DefaultGlobalNotFound, isNotFound } from './not-found'
9
9
  import { isRedirect } from './redirects'
10
10
  import { type AnyRouter, type RegisteredRouter } from './router'
11
+ import { Transitioner } from './Transitioner'
11
12
  import type { ResolveRelativePath, ToOptions } from './link'
12
13
  import type { AnyRoute, ReactNode, StaticDataRouteOption } from './route'
13
14
  import type {
@@ -99,6 +100,21 @@ export type AnyRouteMatch = RouteMatch<any, any, any, any, any, any, any>
99
100
  export function Matches() {
100
101
  const router = useRouter()
101
102
 
103
+ const inner = (
104
+ <>
105
+ <MatchesInner />
106
+ <Transitioner />
107
+ </>
108
+ )
109
+
110
+ return router.options.InnerWrap ? (
111
+ <router.options.InnerWrap>{inner}</router.options.InnerWrap>
112
+ ) : (
113
+ inner
114
+ )
115
+ }
116
+
117
+ function MatchesInner() {
102
118
  const matchId = useRouterState({
103
119
  select: (s) => {
104
120
  return s.matches[0]?.id
@@ -109,7 +125,7 @@ export function Matches() {
109
125
  select: (s) => s.resolvedLocation.state.key!,
110
126
  })
111
127
 
112
- const inner = (
128
+ return (
113
129
  <matchContext.Provider value={matchId}>
114
130
  <CatchBoundary
115
131
  getResetKey={() => resetKey}
@@ -126,12 +142,6 @@ export function Matches() {
126
142
  </CatchBoundary>
127
143
  </matchContext.Provider>
128
144
  )
129
-
130
- return router.options.InnerWrap ? (
131
- <router.options.InnerWrap>{inner}</router.options.InnerWrap>
132
- ) : (
133
- inner
134
- )
135
145
  }
136
146
 
137
147
  function SafeFragment(props: any) {
@@ -1,10 +1,8 @@
1
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
1
2
  import * as React from 'react'
2
- import { flushSync } from 'react-dom'
3
3
  import { Matches } from './Matches'
4
- import { pick, useLayoutEffect } from './utils'
5
- import { useRouter } from './useRouter'
6
- import { useRouterState } from './useRouterState'
7
4
  import { getRouterContext } from './routerContext'
5
+ import { Transitioner } from './Transitioner'
8
6
  import type { NavigateOptions, ToOptions } from './link'
9
7
  import type { ParsedLocation } from './location'
10
8
  import type { AnyRoute } from './route'
@@ -87,17 +85,11 @@ export function RouterContextProvider<
87
85
 
88
86
  const routerContext = getRouterContext()
89
87
 
90
- const pendingElement = router.options.defaultPendingComponent ? (
91
- <router.options.defaultPendingComponent />
92
- ) : null
93
-
94
88
  const provider = (
95
- <React.Suspense fallback={pendingElement}>
96
- <routerContext.Provider value={router}>
97
- {children}
98
- <Transitioner />
99
- </routerContext.Provider>
100
- </React.Suspense>
89
+ <routerContext.Provider value={router}>
90
+ {children}
91
+ <Transitioner />
92
+ </routerContext.Provider>
101
93
  )
102
94
 
103
95
  if (router.options.Wrap) {
@@ -118,129 +110,6 @@ export function RouterProvider<
118
110
  )
119
111
  }
120
112
 
121
- function Transitioner() {
122
- const router = useRouter()
123
- const mountLoadForRouter = React.useRef({ router, mounted: false })
124
- const routerState = useRouterState({
125
- select: (s) =>
126
- pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),
127
- })
128
-
129
- const [isTransitioning, startReactTransition_] = React.useTransition()
130
- // Track pending state changes
131
- const hasPendingMatches = useRouterState({
132
- select: (s) => s.matches.some((d) => d.status === 'pending'),
133
- })
134
-
135
- const previousIsLoading = usePrevious(routerState.isLoading)
136
-
137
- const isAnyPending =
138
- routerState.isLoading || isTransitioning || hasPendingMatches
139
- const previousIsAnyPending = usePrevious(isAnyPending)
140
-
141
- router.startReactTransition = startReactTransition_
142
-
143
- const tryLoad = async () => {
144
- try {
145
- await router.load()
146
- } catch (err) {
147
- console.error(err)
148
- }
149
- }
150
-
151
- // Subscribe to location changes
152
- // and try to load the new location
153
- useLayoutEffect(() => {
154
- const unsub = router.history.subscribe(router.load)
155
-
156
- const nextLocation = router.buildLocation({
157
- to: router.latestLocation.pathname,
158
- search: true,
159
- params: true,
160
- hash: true,
161
- state: true,
162
- })
163
-
164
- if (routerState.location.href !== nextLocation.href) {
165
- router.commitLocation({ ...nextLocation, replace: true })
166
- }
167
-
168
- return () => {
169
- unsub()
170
- }
171
- // eslint-disable-next-line react-hooks/exhaustive-deps
172
- }, [router, router.history])
173
-
174
- // Try to load the initial location
175
- useLayoutEffect(() => {
176
- if (
177
- window.__TSR_DEHYDRATED__ ||
178
- (mountLoadForRouter.current.router === router &&
179
- mountLoadForRouter.current.mounted)
180
- ) {
181
- return
182
- }
183
- mountLoadForRouter.current = { router, mounted: true }
184
- tryLoad()
185
- // eslint-disable-next-line react-hooks/exhaustive-deps
186
- }, [router])
187
-
188
- useLayoutEffect(() => {
189
- // The router was loading and now it's not
190
- if (previousIsLoading && !routerState.isLoading) {
191
- const toLocation = router.state.location
192
- const fromLocation = router.state.resolvedLocation
193
- const pathChanged = fromLocation.href !== toLocation.href
194
-
195
- router.emit({
196
- type: 'onLoad',
197
- fromLocation,
198
- toLocation,
199
- pathChanged,
200
- })
201
-
202
- // if (router.viewTransitionPromise) {
203
- // console.log('resolving view transition promise')
204
- // }
205
-
206
- // router.viewTransitionPromise?.resolve(true)
207
- }
208
- }, [previousIsLoading, router, routerState.isLoading])
209
-
210
- useLayoutEffect(() => {
211
- // The router was pending and now it's not
212
- if (previousIsAnyPending && !isAnyPending) {
213
- const toLocation = router.state.location
214
- const fromLocation = router.state.resolvedLocation
215
- const pathChanged = fromLocation.href !== toLocation.href
216
-
217
- router.emit({
218
- type: 'onResolved',
219
- fromLocation,
220
- toLocation,
221
- pathChanged,
222
- })
223
-
224
- router.__store.setState((s) => ({
225
- ...s,
226
- status: 'idle',
227
- resolvedLocation: s.location,
228
- }))
229
-
230
- if ((document as any).querySelector) {
231
- if (router.state.location.hash !== '') {
232
- const el = document.getElementById(router.state.location.hash)
233
- if (el) {
234
- el.scrollIntoView()
235
- }
236
- }
237
- }
238
- }
239
- }, [isAnyPending, previousIsAnyPending, router])
240
-
241
- return null
242
- }
243
-
244
113
  export function getRouteMatch<TRouteTree extends AnyRoute>(
245
114
  state: RouterState<TRouteTree>,
246
115
  id: string,
@@ -275,15 +144,3 @@ export type RouterProps<
275
144
  >['context']
276
145
  >
277
146
  }
278
-
279
- function usePrevious<T>(value: T): T {
280
- const ref = React.useRef<T>(value)
281
-
282
- if (ref.current !== value) {
283
- const prevValue = ref.current
284
- ref.current = value
285
- return prevValue
286
- } else {
287
- return ref.current
288
- }
289
- }
@@ -0,0 +1,126 @@
1
+ import * as React from 'react'
2
+ import { pick, useLayoutEffect, usePrevious } from './utils'
3
+ import { useRouter } from './useRouter'
4
+ import { useRouterState } from './useRouterState'
5
+
6
+ export function Transitioner() {
7
+ const router = useRouter()
8
+ const mountLoadForRouter = React.useRef({ router, mounted: false })
9
+ const routerState = useRouterState({
10
+ select: (s) =>
11
+ pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning']),
12
+ })
13
+
14
+ const [isTransitioning, startReactTransition_] = React.useTransition()
15
+ // Track pending state changes
16
+ const hasPendingMatches = useRouterState({
17
+ select: (s) => s.matches.some((d) => d.status === 'pending'),
18
+ })
19
+
20
+ const previousIsLoading = usePrevious(routerState.isLoading)
21
+
22
+ const isAnyPending =
23
+ routerState.isLoading || isTransitioning || hasPendingMatches
24
+ const previousIsAnyPending = usePrevious(isAnyPending)
25
+
26
+ router.startReactTransition = startReactTransition_
27
+
28
+ const tryLoad = async () => {
29
+ try {
30
+ await router.load()
31
+ } catch (err) {
32
+ console.error(err)
33
+ }
34
+ }
35
+
36
+ // Subscribe to location changes
37
+ // and try to load the new location
38
+ useLayoutEffect(() => {
39
+ const unsub = router.history.subscribe(router.load)
40
+
41
+ const nextLocation = router.buildLocation({
42
+ to: router.latestLocation.pathname,
43
+ search: true,
44
+ params: true,
45
+ hash: true,
46
+ state: true,
47
+ })
48
+
49
+ if (routerState.location.href !== nextLocation.href) {
50
+ router.commitLocation({ ...nextLocation, replace: true })
51
+ }
52
+
53
+ return () => {
54
+ unsub()
55
+ }
56
+ // eslint-disable-next-line react-hooks/exhaustive-deps
57
+ }, [router, router.history])
58
+
59
+ // Try to load the initial location
60
+ useLayoutEffect(() => {
61
+ if (
62
+ window.__TSR_DEHYDRATED__ ||
63
+ (mountLoadForRouter.current.router === router &&
64
+ mountLoadForRouter.current.mounted)
65
+ ) {
66
+ return
67
+ }
68
+ mountLoadForRouter.current = { router, mounted: true }
69
+ tryLoad()
70
+ // eslint-disable-next-line react-hooks/exhaustive-deps
71
+ }, [router])
72
+
73
+ useLayoutEffect(() => {
74
+ // The router was loading and now it's not
75
+ if (previousIsLoading && !routerState.isLoading) {
76
+ const toLocation = router.state.location
77
+ const fromLocation = router.state.resolvedLocation
78
+ const pathChanged = fromLocation.href !== toLocation.href
79
+
80
+ router.emit({
81
+ type: 'onLoad',
82
+ fromLocation,
83
+ toLocation,
84
+ pathChanged,
85
+ })
86
+
87
+ // if (router.viewTransitionPromise) {
88
+ // console.log('resolving view transition promise')
89
+ // }
90
+ // router.viewTransitionPromise?.resolve(true)
91
+ }
92
+ }, [previousIsLoading, router, routerState.isLoading])
93
+
94
+ useLayoutEffect(() => {
95
+ // The router was pending and now it's not
96
+ if (previousIsAnyPending && !isAnyPending) {
97
+ const toLocation = router.state.location
98
+ const fromLocation = router.state.resolvedLocation
99
+ const pathChanged = fromLocation.href !== toLocation.href
100
+
101
+ router.emit({
102
+ type: 'onResolved',
103
+ fromLocation,
104
+ toLocation,
105
+ pathChanged,
106
+ })
107
+
108
+ router.__store.setState((s) => ({
109
+ ...s,
110
+ status: 'idle',
111
+ resolvedLocation: s.location,
112
+ }))
113
+
114
+ if ((document as any).querySelector) {
115
+ if (router.state.location.hash !== '') {
116
+ const el = document.getElementById(router.state.location.hash)
117
+ if (el) {
118
+ el.scrollIntoView()
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }, [isAnyPending, previousIsAnyPending, router])
124
+
125
+ return null
126
+ }
package/src/awaited.tsx CHANGED
@@ -59,14 +59,15 @@ export function useAwaited<T>({ promise }: AwaitOptions<T>): [T] {
59
59
  // If we are the originator of the promise,
60
60
  // inject the state into the HTML stream
61
61
  if (!isDehydratedDeferred(promise)) {
62
- router.injectHtml(`<script class='tsr_deferred_data'>window.__TSR__DEFERRED__${state.uid} = ${JSON.stringify(router.options.transformer.stringify(state))}</script>
63
- <script class='tsr_deferred_data'>
62
+ router.injectHtml(
63
+ `<script class='tsr_deferred_data'>window.__TSR__DEFERRED__${state.uid} = ${JSON.stringify(router.options.transformer.stringify(state))}
64
64
  if (window.__TSR__ROUTER__) {
65
- let deferred = window.__TSR__ROUTER__.getDeferred('${state.uid}')
66
- if (deferred) deferred.resolve(window.__TSR__DEFERRED__${state.uid})
65
+ let deferred = window.__TSR__ROUTER__.getDeferred('${state.uid}');
66
+ if (deferred) deferred.resolve(window.__TSR__DEFERRED__${state.uid});
67
67
  }
68
- document.querySelectorAll('.tsr_deferred_data').forEach((el) => el.parentElement.removeChild(el))
69
- </script>`)
68
+ document.querySelectorAll('.tsr_deferred_data').forEach((el) => el.parentElement.removeChild(el));
69
+ </script>`,
70
+ )
70
71
  }
71
72
 
72
73
  if (state.status === 'error') {
package/src/utils.ts CHANGED
@@ -352,3 +352,15 @@ export function removeLayoutSegments(routePath: string): string {
352
352
  const newSegments = segments.filter((segment) => !segment.startsWith('_'))
353
353
  return newSegments.join('/')
354
354
  }
355
+
356
+ export function usePrevious<T>(value: T): T {
357
+ const ref = React.useRef<T>(value)
358
+
359
+ if (ref.current !== value) {
360
+ const prevValue = ref.current
361
+ ref.current = value
362
+ return prevValue
363
+ } else {
364
+ return ref.current
365
+ }
366
+ }