@tanstack/solid-router 2.0.0-alpha.6 → 2.0.0-alpha.7

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.
Files changed (219) hide show
  1. package/dist/cjs/Asset.cjs +2 -2
  2. package/dist/cjs/Asset.cjs.map +1 -1
  3. package/dist/cjs/HeadContent.cjs +11 -1
  4. package/dist/cjs/HeadContent.cjs.map +1 -1
  5. package/dist/cjs/HeadContent.dev.cjs +11 -1
  6. package/dist/cjs/HeadContent.dev.cjs.map +1 -1
  7. package/dist/cjs/Match.cjs +265 -248
  8. package/dist/cjs/Match.cjs.map +1 -1
  9. package/dist/cjs/Match.d.cts +1 -3
  10. package/dist/cjs/Matches.cjs +35 -34
  11. package/dist/cjs/Matches.cjs.map +1 -1
  12. package/dist/cjs/RouterProvider.cjs +12 -8
  13. package/dist/cjs/RouterProvider.cjs.map +1 -1
  14. package/dist/cjs/RouterProvider.d.cts +1 -1
  15. package/dist/cjs/Scripts.cjs +23 -12
  16. package/dist/cjs/Scripts.cjs.map +1 -1
  17. package/dist/cjs/Scripts.d.cts +2 -1
  18. package/dist/cjs/Transitioner.cjs +55 -34
  19. package/dist/cjs/Transitioner.cjs.map +1 -1
  20. package/dist/cjs/headContentUtils.cjs +26 -23
  21. package/dist/cjs/headContentUtils.cjs.map +1 -1
  22. package/dist/cjs/headContentUtils.d.cts +2 -1
  23. package/dist/cjs/index.cjs +1 -1
  24. package/dist/cjs/index.dev.cjs +1 -1
  25. package/dist/cjs/link.cjs +143 -101
  26. package/dist/cjs/link.cjs.map +1 -1
  27. package/dist/cjs/matchContext.cjs +7 -5
  28. package/dist/cjs/matchContext.cjs.map +1 -1
  29. package/dist/cjs/matchContext.d.cts +8 -2
  30. package/dist/cjs/not-found.cjs +8 -4
  31. package/dist/cjs/not-found.cjs.map +1 -1
  32. package/dist/cjs/not-found.d.cts +1 -1
  33. package/dist/cjs/router.cjs +2 -1
  34. package/dist/cjs/router.cjs.map +1 -1
  35. package/dist/cjs/routerStores.cjs +75 -0
  36. package/dist/cjs/routerStores.cjs.map +1 -0
  37. package/dist/cjs/routerStores.d.cts +10 -0
  38. package/dist/cjs/ssr/RouterClient.cjs +1 -1
  39. package/dist/cjs/ssr/RouterClient.cjs.map +1 -1
  40. package/dist/cjs/ssr/renderRouterToStream.cjs +1 -1
  41. package/dist/cjs/ssr/renderRouterToStream.cjs.map +1 -1
  42. package/dist/cjs/ssr/renderRouterToString.cjs +1 -1
  43. package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -1
  44. package/dist/cjs/useBlocker.cjs +12 -3
  45. package/dist/cjs/useBlocker.cjs.map +1 -1
  46. package/dist/cjs/useCanGoBack.cjs +6 -2
  47. package/dist/cjs/useCanGoBack.cjs.map +1 -1
  48. package/dist/cjs/useCanGoBack.d.cts +2 -1
  49. package/dist/cjs/useLoaderDeps.cjs +2 -3
  50. package/dist/cjs/useLoaderDeps.cjs.map +1 -1
  51. package/dist/cjs/useLocation.cjs +13 -2
  52. package/dist/cjs/useLocation.cjs.map +1 -1
  53. package/dist/cjs/useMatch.cjs +27 -15
  54. package/dist/cjs/useMatch.cjs.map +1 -1
  55. package/dist/cjs/useParams.cjs +1 -1
  56. package/dist/cjs/useParams.cjs.map +1 -1
  57. package/dist/cjs/useRouterState.cjs +12 -30
  58. package/dist/cjs/useRouterState.cjs.map +1 -1
  59. package/dist/cjs/useSearch.cjs +2 -1
  60. package/dist/cjs/useSearch.cjs.map +1 -1
  61. package/dist/cjs/utils.cjs +3 -17
  62. package/dist/cjs/utils.cjs.map +1 -1
  63. package/dist/cjs/utils.d.cts +0 -5
  64. package/dist/esm/Asset.js +6 -6
  65. package/dist/esm/Asset.js.map +1 -1
  66. package/dist/esm/HeadContent.dev.js +12 -2
  67. package/dist/esm/HeadContent.dev.js.map +1 -1
  68. package/dist/esm/HeadContent.js +12 -2
  69. package/dist/esm/HeadContent.js.map +1 -1
  70. package/dist/esm/Match.d.ts +1 -3
  71. package/dist/esm/Match.js +267 -250
  72. package/dist/esm/Match.js.map +1 -1
  73. package/dist/esm/Matches.js +40 -39
  74. package/dist/esm/Matches.js.map +1 -1
  75. package/dist/esm/RouterProvider.d.ts +1 -1
  76. package/dist/esm/RouterProvider.js +10 -7
  77. package/dist/esm/RouterProvider.js.map +1 -1
  78. package/dist/esm/ScriptOnce.js +2 -2
  79. package/dist/esm/ScriptOnce.js.map +1 -1
  80. package/dist/esm/Scripts.d.ts +2 -1
  81. package/dist/esm/Scripts.js +21 -11
  82. package/dist/esm/Scripts.js.map +1 -1
  83. package/dist/esm/Transitioner.js +56 -35
  84. package/dist/esm/Transitioner.js.map +1 -1
  85. package/dist/esm/headContentUtils.d.ts +2 -1
  86. package/dist/esm/headContentUtils.js +26 -23
  87. package/dist/esm/headContentUtils.js.map +1 -1
  88. package/dist/esm/index.dev.js +1 -1
  89. package/dist/esm/index.js +1 -1
  90. package/dist/esm/link.js +146 -104
  91. package/dist/esm/link.js.map +1 -1
  92. package/dist/esm/matchContext.d.ts +8 -2
  93. package/dist/esm/matchContext.js +7 -4
  94. package/dist/esm/matchContext.js.map +1 -1
  95. package/dist/esm/not-found.d.ts +1 -1
  96. package/dist/esm/not-found.js +6 -3
  97. package/dist/esm/not-found.js.map +1 -1
  98. package/dist/esm/router.js +2 -1
  99. package/dist/esm/router.js.map +1 -1
  100. package/dist/esm/routerStores.d.ts +10 -0
  101. package/dist/esm/routerStores.js +73 -0
  102. package/dist/esm/routerStores.js.map +1 -0
  103. package/dist/esm/scroll-restoration.js +2 -2
  104. package/dist/esm/scroll-restoration.js.map +1 -1
  105. package/dist/esm/ssr/RouterClient.js +1 -1
  106. package/dist/esm/ssr/RouterClient.js.map +1 -1
  107. package/dist/esm/ssr/renderRouterToStream.js +1 -1
  108. package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
  109. package/dist/esm/ssr/renderRouterToString.js +1 -1
  110. package/dist/esm/ssr/renderRouterToString.js.map +1 -1
  111. package/dist/esm/useBlocker.js +12 -3
  112. package/dist/esm/useBlocker.js.map +1 -1
  113. package/dist/esm/useCanGoBack.d.ts +2 -1
  114. package/dist/esm/useCanGoBack.js +4 -2
  115. package/dist/esm/useCanGoBack.js.map +1 -1
  116. package/dist/esm/useLoaderDeps.js +2 -3
  117. package/dist/esm/useLoaderDeps.js.map +1 -1
  118. package/dist/esm/useLocation.js +11 -2
  119. package/dist/esm/useLocation.js.map +1 -1
  120. package/dist/esm/useMatch.js +28 -16
  121. package/dist/esm/useMatch.js.map +1 -1
  122. package/dist/esm/useParams.js +1 -1
  123. package/dist/esm/useParams.js.map +1 -1
  124. package/dist/esm/useRouterState.js +11 -30
  125. package/dist/esm/useRouterState.js.map +1 -1
  126. package/dist/esm/useSearch.js +2 -1
  127. package/dist/esm/useSearch.js.map +1 -1
  128. package/dist/esm/utils.d.ts +0 -5
  129. package/dist/esm/utils.js +4 -17
  130. package/dist/esm/utils.js.map +1 -1
  131. package/dist/source/Asset.jsx +3 -3
  132. package/dist/source/Asset.jsx.map +1 -1
  133. package/dist/source/HeadContent.dev.jsx +5 -1
  134. package/dist/source/HeadContent.dev.jsx.map +1 -1
  135. package/dist/source/HeadContent.jsx +5 -1
  136. package/dist/source/HeadContent.jsx.map +1 -1
  137. package/dist/source/Match.d.ts +1 -3
  138. package/dist/source/Match.jsx +260 -264
  139. package/dist/source/Match.jsx.map +1 -1
  140. package/dist/source/Matches.jsx +46 -46
  141. package/dist/source/Matches.jsx.map +1 -1
  142. package/dist/source/RouterProvider.d.ts +1 -1
  143. package/dist/source/RouterProvider.jsx +13 -9
  144. package/dist/source/RouterProvider.jsx.map +1 -1
  145. package/dist/source/Scripts.d.ts +2 -1
  146. package/dist/source/Scripts.jsx +46 -47
  147. package/dist/source/Scripts.jsx.map +1 -1
  148. package/dist/source/Transitioner.jsx +78 -42
  149. package/dist/source/Transitioner.jsx.map +1 -1
  150. package/dist/source/headContentUtils.d.ts +2 -1
  151. package/dist/source/headContentUtils.jsx +79 -80
  152. package/dist/source/headContentUtils.jsx.map +1 -1
  153. package/dist/source/link.jsx +145 -112
  154. package/dist/source/link.jsx.map +1 -1
  155. package/dist/source/matchContext.d.ts +8 -2
  156. package/dist/source/matchContext.jsx +7 -3
  157. package/dist/source/matchContext.jsx.map +1 -1
  158. package/dist/source/not-found.d.ts +1 -1
  159. package/dist/source/not-found.jsx +6 -5
  160. package/dist/source/not-found.jsx.map +1 -1
  161. package/dist/source/router.js +2 -1
  162. package/dist/source/router.js.map +1 -1
  163. package/dist/source/routerStores.d.ts +10 -0
  164. package/dist/source/routerStores.js +82 -0
  165. package/dist/source/routerStores.js.map +1 -0
  166. package/dist/source/ssr/RouterClient.jsx +1 -1
  167. package/dist/source/ssr/RouterClient.jsx.map +1 -1
  168. package/dist/source/ssr/renderRouterToStream.jsx +1 -1
  169. package/dist/source/ssr/renderRouterToStream.jsx.map +1 -1
  170. package/dist/source/ssr/renderRouterToString.jsx +1 -1
  171. package/dist/source/ssr/renderRouterToString.jsx.map +1 -1
  172. package/dist/source/useBlocker.jsx +19 -8
  173. package/dist/source/useBlocker.jsx.map +1 -1
  174. package/dist/source/useCanGoBack.d.ts +2 -1
  175. package/dist/source/useCanGoBack.js +4 -2
  176. package/dist/source/useCanGoBack.js.map +1 -1
  177. package/dist/source/useLoaderDeps.jsx +2 -3
  178. package/dist/source/useLoaderDeps.jsx.map +1 -1
  179. package/dist/source/useLocation.jsx +13 -3
  180. package/dist/source/useLocation.jsx.map +1 -1
  181. package/dist/source/useMatch.jsx +33 -23
  182. package/dist/source/useMatch.jsx.map +1 -1
  183. package/dist/source/useParams.jsx +1 -1
  184. package/dist/source/useParams.jsx.map +1 -1
  185. package/dist/source/useRouterState.jsx +14 -55
  186. package/dist/source/useRouterState.jsx.map +1 -1
  187. package/dist/source/useSearch.jsx +2 -1
  188. package/dist/source/useSearch.jsx.map +1 -1
  189. package/dist/source/utils.d.ts +0 -5
  190. package/dist/source/utils.js +2 -15
  191. package/dist/source/utils.js.map +1 -1
  192. package/package.json +2 -2
  193. package/skills/solid-router/SKILL.md +2 -0
  194. package/src/Asset.tsx +3 -3
  195. package/src/HeadContent.dev.tsx +10 -1
  196. package/src/HeadContent.tsx +10 -1
  197. package/src/Match.tsx +395 -349
  198. package/src/Matches.tsx +55 -54
  199. package/src/RouterProvider.tsx +13 -10
  200. package/src/Scripts.tsx +55 -54
  201. package/src/Transitioner.tsx +101 -58
  202. package/src/headContentUtils.tsx +104 -96
  203. package/src/link.tsx +188 -146
  204. package/src/matchContext.tsx +16 -7
  205. package/src/not-found.tsx +6 -6
  206. package/src/router.ts +2 -1
  207. package/src/routerStores.ts +119 -0
  208. package/src/ssr/RouterClient.tsx +1 -1
  209. package/src/ssr/renderRouterToStream.tsx +1 -1
  210. package/src/ssr/renderRouterToString.tsx +1 -1
  211. package/src/useBlocker.tsx +80 -63
  212. package/src/useCanGoBack.ts +6 -2
  213. package/src/useLoaderDeps.tsx +2 -3
  214. package/src/useLocation.tsx +18 -5
  215. package/src/useMatch.tsx +37 -38
  216. package/src/useParams.tsx +2 -3
  217. package/src/useRouterState.tsx +21 -67
  218. package/src/useSearch.tsx +2 -1
  219. package/src/utils.ts +2 -24
@@ -180,86 +180,103 @@ export function useBlocker(
180
180
  reset: undefined,
181
181
  })
182
182
 
183
- Solid.createTrackedEffect(() => {
184
- const blockerFnComposed = async (blockerFnArgs: BlockerFnArgs) => {
185
- function getLocation(
186
- location: HistoryLocation,
187
- ): AnyShouldBlockFnLocation {
188
- const parsedLocation = router.parseLocation(location)
189
- const matchedRoutes = router.getMatchedRoutes(parsedLocation.pathname)
190
- if (matchedRoutes.foundRoute === undefined) {
183
+ let disposeBlock: (() => void) | undefined
184
+
185
+ Solid.createEffect(
186
+ () => props.disabled,
187
+ (disabled) => {
188
+ // Dispose previous blocker registration when re-running
189
+ disposeBlock?.()
190
+ disposeBlock = undefined
191
+
192
+ if (disabled) {
193
+ return
194
+ }
195
+
196
+ const blockerFnComposed = async (blockerFnArgs: BlockerFnArgs) => {
197
+ function getLocation(
198
+ location: HistoryLocation,
199
+ ): AnyShouldBlockFnLocation {
200
+ const parsedLocation = router.parseLocation(location)
201
+ const matchedRoutes = router.getMatchedRoutes(parsedLocation.pathname)
202
+ if (matchedRoutes.foundRoute === undefined) {
203
+ return {
204
+ routeId: '__notFound__',
205
+ fullPath: parsedLocation.pathname,
206
+ pathname: parsedLocation.pathname,
207
+ params: matchedRoutes.routeParams,
208
+ search: parsedLocation.search,
209
+ }
210
+ }
191
211
  return {
192
- routeId: '__notFound__',
193
- fullPath: parsedLocation.pathname,
212
+ routeId: matchedRoutes.foundRoute.id,
213
+ fullPath: matchedRoutes.foundRoute.fullPath,
194
214
  pathname: parsedLocation.pathname,
195
215
  params: matchedRoutes.routeParams,
196
216
  search: parsedLocation.search,
197
217
  }
198
218
  }
199
- return {
200
- routeId: matchedRoutes.foundRoute.id,
201
- fullPath: matchedRoutes.foundRoute.fullPath,
202
- pathname: parsedLocation.pathname,
203
- params: matchedRoutes.routeParams,
204
- search: parsedLocation.search,
205
- }
206
- }
207
-
208
- const current = getLocation(blockerFnArgs.currentLocation)
209
- const next = getLocation(blockerFnArgs.nextLocation)
210
219
 
211
- if (
212
- current.routeId === '__notFound__' &&
213
- next.routeId !== '__notFound__'
214
- ) {
215
- return false
216
- }
217
-
218
- const shouldBlock = await props.shouldBlockFn({
219
- action: blockerFnArgs.action,
220
- current,
221
- next,
222
- })
223
- if (!props.withResolver) {
224
- return shouldBlock
225
- }
220
+ const current = getLocation(blockerFnArgs.currentLocation)
221
+ const next = getLocation(blockerFnArgs.nextLocation)
226
222
 
227
- if (!shouldBlock) {
228
- return false
229
- }
223
+ if (
224
+ current.routeId === '__notFound__' &&
225
+ next.routeId !== '__notFound__'
226
+ ) {
227
+ return false
228
+ }
230
229
 
231
- const promise = new Promise<boolean>((resolve) => {
232
- setResolver({
233
- status: 'blocked',
230
+ const shouldBlock = await props.shouldBlockFn({
231
+ action: blockerFnArgs.action,
234
232
  current,
235
233
  next,
236
- action: blockerFnArgs.action,
237
- proceed: () => resolve(false),
238
- reset: () => resolve(true),
239
234
  })
240
- })
235
+ if (!props.withResolver) {
236
+ return shouldBlock
237
+ }
241
238
 
242
- const canNavigateAsync = await promise
243
- setResolver({
244
- status: 'idle',
245
- current: undefined,
246
- next: undefined,
247
- action: undefined,
248
- proceed: undefined,
249
- reset: undefined,
250
- })
239
+ if (!shouldBlock) {
240
+ return false
241
+ }
251
242
 
252
- return canNavigateAsync
253
- }
243
+ const promise = new Promise<boolean>((resolve) => {
244
+ setResolver({
245
+ status: 'blocked',
246
+ current,
247
+ next,
248
+ action: blockerFnArgs.action,
249
+ proceed: () => resolve(false),
250
+ reset: () => resolve(true),
251
+ })
252
+ })
254
253
 
255
- const disposeBlock = props.disabled
256
- ? undefined
257
- : router.history.block({
258
- blockerFn: blockerFnComposed,
259
- enableBeforeUnload: props.enableBeforeUnload,
254
+ const canNavigateAsync = await promise
255
+ setResolver({
256
+ status: 'idle',
257
+ current: undefined,
258
+ next: undefined,
259
+ action: undefined,
260
+ proceed: undefined,
261
+ reset: undefined,
260
262
  })
261
263
 
262
- return () => disposeBlock?.()
264
+ return canNavigateAsync
265
+ }
266
+
267
+ disposeBlock = router.history.block({
268
+ blockerFn: blockerFnComposed,
269
+ enableBeforeUnload: props.enableBeforeUnload,
270
+ })
271
+ },
272
+ )
273
+
274
+ // Clean up on unmount
275
+ Solid.onSettled(() => {
276
+ return () => {
277
+ disposeBlock?.()
278
+ disposeBlock = undefined
279
+ }
263
280
  })
264
281
 
265
282
  return resolver
@@ -1,5 +1,9 @@
1
- import { useRouterState } from './useRouterState'
1
+ import * as Solid from 'solid-js'
2
+ import { useRouter } from './useRouter'
2
3
 
3
4
  export function useCanGoBack() {
4
- return useRouterState({ select: (s) => s.location.state.__TSR_index !== 0 })
5
+ const router = useRouter()
6
+ return Solid.createMemo(
7
+ () => router.stores.location.state.state.__TSR_index !== 0,
8
+ )
5
9
  }
@@ -37,11 +37,10 @@ export function useLoaderDeps<
37
37
  >(
38
38
  opts: UseLoaderDepsOptions<TRouter, TFrom, TSelected>,
39
39
  ): Accessor<UseLoaderDepsResult<TRouter, TFrom, TSelected>> {
40
- const { select, ...rest } = opts
41
40
  return useMatch({
42
- ...rest,
41
+ ...opts,
43
42
  select: (s) => {
44
- return select ? select(s.loaderDeps) : s.loaderDeps
43
+ return opts.select ? opts.select(s.loaderDeps) : s.loaderDeps
45
44
  },
46
45
  }) as Accessor<UseLoaderDepsResult<TRouter, TFrom, TSelected>>
47
46
  }
@@ -1,4 +1,6 @@
1
- import { useRouterState } from './useRouterState'
1
+ import * as Solid from 'solid-js'
2
+ import { replaceEqualDeep } from '@tanstack/router-core'
3
+ import { useRouter } from './useRouter'
2
4
  import type {
3
5
  AnyRouter,
4
6
  RegisteredRouter,
@@ -23,8 +25,19 @@ export function useLocation<
23
25
  >(
24
26
  opts?: UseLocationBaseOptions<TRouter, TSelected>,
25
27
  ): Accessor<UseLocationResult<TRouter, TSelected>> {
26
- return useRouterState({
27
- select: (state: any) =>
28
- opts?.select ? opts.select(state.location) : state.location,
29
- } as any) as Accessor<UseLocationResult<TRouter, TSelected>>
28
+ const router = useRouter<TRouter>()
29
+
30
+ if (!opts?.select) {
31
+ return (() => router.stores.location.state) as Accessor<
32
+ UseLocationResult<TRouter, TSelected>
33
+ >
34
+ }
35
+
36
+ const select = opts.select
37
+
38
+ return Solid.createMemo((prev: TSelected | undefined) => {
39
+ const res = select(router.stores.location.state)
40
+ if (prev === undefined) return res
41
+ return replaceEqualDeep(prev, res)
42
+ }) as Accessor<UseLocationResult<TRouter, TSelected>>
30
43
  }
package/src/useMatch.tsx CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as Solid from 'solid-js'
2
2
  import invariant from 'tiny-invariant'
3
- import { useRouterState } from './useRouterState'
4
- import { dummyMatchContext, matchContext } from './matchContext'
3
+ import { replaceEqualDeep } from '@tanstack/router-core'
4
+ import { nearestMatchContext } from './matchContext'
5
+ import { useRouter } from './useRouter'
5
6
  import type {
6
7
  AnyRouter,
7
8
  MakeRouteMatch,
@@ -69,47 +70,45 @@ export function useMatch<
69
70
  ): Solid.Accessor<
70
71
  ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow>
71
72
  > {
72
- const nearestMatchId = Solid.useContext(
73
- opts.from ? dummyMatchContext : matchContext,
74
- )
73
+ const router = useRouter<TRouter>()
74
+ const nearestMatch = opts.from
75
+ ? undefined
76
+ : Solid.useContext(nearestMatchContext)
75
77
 
76
- // Create a signal to track error state separately from the match
77
- const matchState: Solid.Accessor<{
78
- match: any
79
- shouldThrowError: boolean
80
- }> = useRouterState({
81
- select: (state: any) => {
82
- const match = state.matches.find((d: any) =>
83
- opts.from ? opts.from === d.routeId : d.id === nearestMatchId(),
84
- )
78
+ const match = () => {
79
+ if (opts.from) {
80
+ const ids = router.stores.matchesId.state
81
+ for (const id of ids) {
82
+ const matchStore = router.stores.activeMatchStoresById.get(id)
83
+ if (matchStore?.routeId === opts.from) {
84
+ return matchStore.state
85
+ }
86
+ }
85
87
 
86
- if (match === undefined) {
87
- // During navigation transitions, check if the match exists in pendingMatches
88
- const pendingMatch = state.pendingMatches?.find((d: any) =>
89
- opts.from ? opts.from === d.routeId : d.id === nearestMatchId(),
90
- )
88
+ return undefined
89
+ }
91
90
 
92
- // Determine if we should throw an error
93
- const shouldThrowError =
94
- !pendingMatch && !state.isTransitioning && (opts.shouldThrow ?? true)
91
+ return nearestMatch?.match()
92
+ }
95
93
 
96
- return { match: undefined, shouldThrowError }
97
- }
94
+ return Solid.createMemo((prev: TSelected | undefined) => {
95
+ const selectedMatch = match()
98
96
 
99
- return {
100
- match: opts.select ? opts.select(match) : match,
101
- shouldThrowError: false,
102
- }
103
- },
104
- } as any)
97
+ if (selectedMatch === undefined) {
98
+ const hasPendingMatch = opts.from
99
+ ? Boolean(router.stores.pendingRouteIds.state[opts.from!])
100
+ : (nearestMatch?.hasPending() ?? false)
101
+ const isTransitioning = router.stores.isTransitioning.state
105
102
 
106
- if (Solid.untrack(matchState).shouldThrowError) {
107
- invariant(
108
- false,
109
- `Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
110
- )
111
- }
103
+ invariant(
104
+ !(!hasPendingMatch && !isTransitioning && (opts.shouldThrow ?? true)),
105
+ `Could not find ${opts.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`,
106
+ )
107
+ return undefined
108
+ }
112
109
 
113
- // Return an accessor that extracts just the match value
114
- return Solid.createMemo(() => matchState().match) as any
110
+ const res = opts.select ? opts.select(selectedMatch as any) : selectedMatch
111
+ if (prev === undefined) return res as TSelected
112
+ return replaceEqualDeep(prev, res) as TSelected
113
+ }) as any
115
114
  }
package/src/useParams.tsx CHANGED
@@ -62,11 +62,10 @@ export function useParams<
62
62
  > {
63
63
  return useMatch({
64
64
  from: opts.from!,
65
- shouldThrow: opts.shouldThrow,
66
65
  strict: opts.strict,
67
- select: (match) => {
66
+ shouldThrow: opts.shouldThrow,
67
+ select: (match: any) => {
68
68
  const params = opts.strict === false ? match.params : match._strictParams
69
-
70
69
  return opts.select ? opts.select(params) : params
71
70
  },
72
71
  }) as Accessor<any>
@@ -1,5 +1,6 @@
1
- import { createMemo, createSignal, onCleanup } from 'solid-js'
2
1
  import { isServer } from '@tanstack/router-core/isServer'
2
+ import * as Solid from 'solid-js'
3
+ import { replaceEqualDeep } from '@tanstack/router-core'
3
4
  import { useRouter } from './useRouter'
4
5
  import type {
5
6
  AnyRouter,
@@ -8,41 +9,6 @@ import type {
8
9
  } from '@tanstack/router-core'
9
10
  import type { Accessor } from 'solid-js'
10
11
 
11
- function deepEqual(a: any, b: any): boolean {
12
- if (Object.is(a, b)) return true
13
-
14
- if (isPromiseLike(a) || isPromiseLike(b)) return false
15
-
16
- if (
17
- typeof a !== 'object' ||
18
- a === null ||
19
- typeof b !== 'object' ||
20
- b === null
21
- ) {
22
- return false
23
- }
24
-
25
- const keysA = Object.keys(a)
26
- const keysB = Object.keys(b)
27
-
28
- if (keysA.length !== keysB.length) return false
29
-
30
- for (const key of keysA) {
31
- if (!Object.prototype.hasOwnProperty.call(b, key)) return false
32
- if (!deepEqual(a[key], b[key])) return false
33
- }
34
-
35
- return true
36
- }
37
-
38
- function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
39
- return (
40
- !!value &&
41
- (typeof value === 'object' || typeof value === 'function') &&
42
- typeof (value as PromiseLike<unknown>).then === 'function'
43
- )
44
- }
45
-
46
12
  export type UseRouterStateOptions<TRouter extends AnyRouter, TSelected> = {
47
13
  router?: TRouter
48
14
  select?: (state: RouterState<TRouter['routeTree']>) => TSelected
@@ -69,40 +35,28 @@ export function useRouterState<
69
35
  // implementation does not provide subscribe() semantics.
70
36
  const _isServer = isServer ?? router.isServer
71
37
  if (_isServer) {
72
- const state = router.state as RouterState<TRouter['routeTree']>
73
- const selected = createMemo(() =>
74
- opts?.select ? opts.select(state) : state,
75
- ) as Accessor<UseRouterStateResult<TRouter, TSelected>>
76
- return selected
38
+ const state = router.stores.__store.state as RouterState<
39
+ TRouter['routeTree']
40
+ >
41
+ const selected = (
42
+ opts?.select ? opts.select(state) : state
43
+ ) as UseRouterStateResult<TRouter, TSelected>
44
+ return (() => selected) as Accessor<
45
+ UseRouterStateResult<TRouter, TSelected>
46
+ >
77
47
  }
78
48
 
79
- const selector = (state: any) => {
80
- if (opts?.select) return opts.select(state)
81
-
82
- return state
49
+ if (!opts?.select) {
50
+ return (() => router.stores.__store.state) as Accessor<
51
+ UseRouterStateResult<TRouter, TSelected>
52
+ >
83
53
  }
84
54
 
85
- // Track the latest store state in a signal that updates via subscription.
86
- // We store the full state so that the selector (which may read reactive
87
- // props like props.matchId) re-runs inside a Solid tracking scope (the
88
- // createMemo below) rather than inside the store subscriber callback
89
- // where reactive reads would be untracked.
90
- const [storeState, setStoreState] = createSignal(router.__store.get())
91
-
92
- const unsub = router.__store.subscribe((s) => {
93
- setStoreState(s)
94
- }).unsubscribe
95
-
96
- onCleanup(() => {
97
- unsub()
98
- })
99
-
100
- // Run the selector inside a memo so that:
101
- // 1. Reactive values read by the selector (e.g. props.matchId) are tracked
102
- // 2. The result is memoized and only updates when the selected value changes
103
- const selected = createMemo(() => selector(storeState()), undefined, {
104
- equals: (a: any, b: any) => deepEqual(a, b),
105
- })
55
+ const select = opts.select
106
56
 
107
- return selected as Accessor<UseRouterStateResult<TRouter, TSelected>>
57
+ return Solid.createMemo((prev: TSelected | undefined) => {
58
+ const res = select(router.stores.__store.state)
59
+ if (prev === undefined) return res
60
+ return replaceEqualDeep(prev, res)
61
+ }) as Accessor<UseRouterStateResult<TRouter, TSelected>>
108
62
  }
package/src/useSearch.tsx CHANGED
@@ -65,7 +65,8 @@ export function useSearch<
65
65
  strict: opts.strict,
66
66
  shouldThrow: opts.shouldThrow,
67
67
  select: (match: any) => {
68
- return opts.select ? opts.select(match.search) : match.search
68
+ const search = match.search
69
+ return opts.select ? opts.select(search) : search
69
70
  },
70
71
  }) as any
71
72
  }
package/src/utils.ts CHANGED
@@ -1,27 +1,5 @@
1
1
  import * as Solid from 'solid-js'
2
2
 
3
- export const useLayoutEffect =
4
- typeof window !== 'undefined' ? Solid.createEffect : Solid.createEffect
5
-
6
- export const usePrevious = (fn: () => boolean) => {
7
- return Solid.createMemo(
8
- (
9
- prev: { current: boolean | null; previous: boolean | null } = {
10
- current: null,
11
- previous: null,
12
- },
13
- ) => {
14
- const current = fn()
15
-
16
- if (prev.current !== current) {
17
- return { previous: prev.current, current }
18
- }
19
-
20
- return prev
21
- },
22
- )
23
- }
24
-
25
3
  /**
26
4
  * React hook to wrap `IntersectionObserver`.
27
5
  *
@@ -68,9 +46,9 @@ export function useIntersectionObserver<T extends Element>(
68
46
 
69
47
  observerRef.observe(r)
70
48
 
71
- Solid.onCleanup(() => {
49
+ return () => {
72
50
  observerRef?.disconnect()
73
- })
51
+ }
74
52
  })
75
53
 
76
54
  return () => observerRef