Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/warm-stores-select.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@tanstack/react-router': patch
'@tanstack/vue-router': patch
---

Migrate to TanStack Store 0.11.1 and preserve subscription cleanup for Vue functional components.
2 changes: 1 addition & 1 deletion packages/react-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
},
"dependencies": {
"@tanstack/history": "workspace:*",
"@tanstack/react-store": "^0.9.3",
"@tanstack/react-store": "^0.11.1",
"@tanstack/router-core": "workspace:*",
"isbot": "^5.1.22"
},
Expand Down
10 changes: 5 additions & 5 deletions packages/react-router/src/Match.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import * as React from 'react'
import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { isNotFound, rootRouteId } from '@tanstack/router-core'
import { isServer } from '@tanstack/router-core/isServer'
import { CatchBoundary, ErrorComponent } from './CatchBoundary'
Expand Down Expand Up @@ -51,7 +51,7 @@ export const Match = React.memo(function MatchImpl({

const matchStore = router.stores.getMatchStore(routeId)
// eslint-disable-next-line react-hooks/rules-of-hooks
const match = useStore(matchStore, (value) => value)
const match = useSelector(matchStore)
return <MatchView router={router} match={match!} />
})

Expand Down Expand Up @@ -238,14 +238,14 @@ export const Outlet = React.memo(function OutletImpl() {
const parentMatchStore = router.stores.getMatchStore(routeId)

// eslint-disable-next-line react-hooks/rules-of-hooks
;[parentGlobalNotFound, parentNotFoundError] = useStore(
;[parentGlobalNotFound, parentNotFoundError] = useSelector(
parentMatchStore,
(match): OutletMatchSelection => [!!match!._notFound, match!.error],
outletMatchSelectionEqual,
{ compare: outletMatchSelectionEqual },
)

// eslint-disable-next-line react-hooks/rules-of-hooks
childRouteId = useStore(router.stores.ids, (ids) => {
childRouteId = useSelector(router.stores.ids, (ids) => {
return ids[ids.indexOf(routeId) + 1]
})
}
Expand Down
12 changes: 6 additions & 6 deletions packages/react-router/src/Matches.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import * as React from 'react'
import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { rootRouteId } from '@tanstack/router-core'
import { isServer } from '@tanstack/router-core/isServer'
import { CatchBoundary } from './CatchBoundary'
Expand Down Expand Up @@ -87,7 +87,7 @@ function MatchesInner() {
(isServer ?? router.isServer)
? router.stores.matches.get()
: // eslint-disable-next-line react-hooks/rules-of-hooks
useStore(
useSelector(
router.stores.matches,
(value) => acknowledgement[0 /* offered */] ?? value,
)
Expand Down Expand Up @@ -156,11 +156,11 @@ export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {

if (!(isServer ?? router.isServer)) {
// eslint-disable-next-line react-hooks/rules-of-hooks
useStore(router.stores.location, (location) => location.href)
useSelector(router.stores.location, (location) => location.href)
// eslint-disable-next-line react-hooks/rules-of-hooks
useStore(router.stores.resolvedLocation, (location) => location?.href)
useSelector(router.stores.resolvedLocation, (location) => location?.href)
// eslint-disable-next-line react-hooks/rules-of-hooks
useStore(router.stores.status, (status) => status)
useSelector(router.stores.status)
}

return React.useCallback(
Expand Down Expand Up @@ -264,7 +264,7 @@ export function useMatches<
}

// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
return useStore(
return useSelector(
router.stores.matches,
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
useStructuralSharing(opts, router),
Expand Down
6 changes: 4 additions & 2 deletions packages/react-router/src/Scripts.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { _getAssetMatches, deepEqual } from '@tanstack/router-core'
import { isServer } from '@tanstack/router-core/isServer'
import { Asset } from './Asset'
Expand Down Expand Up @@ -69,7 +69,9 @@ export const Scripts = () => {
}

// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
const scripts = useStore(router.stores.matches, getScripts, deepEqual)
const scripts = useSelector(router.stores.matches, getScripts, {
compare: deepEqual,
})

return renderScripts(router, scripts)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/react-router/src/headContentUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import {
_getAssetMatches,
appendUniqueUserTags,
Expand Down Expand Up @@ -212,5 +212,5 @@ export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => {
[assetCrossOrigin, nonce, router],
)
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
return useStore(router.stores.matches, selectTags, deepEqual)
return useSelector(router.stores.matches, selectTags, { compare: deepEqual })
}
10 changes: 4 additions & 6 deletions packages/react-router/src/link.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import * as React from 'react'
import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { flushSync } from 'react-dom'
import {
deepEqual,
Expand Down Expand Up @@ -400,11 +400,9 @@ export function useLinkProps<
)

// eslint-disable-next-line react-hooks/rules-of-hooks
const currentLocation = useStore(
router.stores.location,
(l) => l,
(prev, next) => prev.href === next.href,
)
const currentLocation = useSelector(router.stores.location, (l) => l, {
compare: (prev, next) => prev.href === next.href,
})

// eslint-disable-next-line react-hooks/rules-of-hooks
const next = React.useMemo(() => {
Expand Down
6 changes: 3 additions & 3 deletions packages/react-router/src/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react'
import { isNotFound } from '@tanstack/router-core'
import { isServer } from '@tanstack/router-core/isServer'
import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { CatchBoundary } from './CatchBoundary'
import { useRouter } from './useRouter'
import type { ErrorInfo } from 'react'
Expand Down Expand Up @@ -44,12 +44,12 @@ export function CatchNotFound(props: {

// TODO: Some way for the user to programmatically reset the not-found boundary?
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
const pathname = useStore(
const pathname = useSelector(
router.stores.location,
(location) => location.pathname,
)
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
const status = useStore(router.stores.status, (status) => status)
const status = useSelector(router.stores.status)
const resetKey = `not-found-${pathname}-${status}`

return (
Expand Down
4 changes: 2 additions & 2 deletions packages/react-router/src/useCanGoBack.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { isServer } from '@tanstack/router-core/isServer'
import { useRouter } from './useRouter'

Expand All @@ -10,7 +10,7 @@ export function useCanGoBack() {
}

// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
return useStore(
return useSelector(
router.stores.location,
(location) => location.state.__TSR_index !== 0,
)
Expand Down
4 changes: 2 additions & 2 deletions packages/react-router/src/useLocation.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { isServer } from '@tanstack/router-core/isServer'
import { useRouter } from './useRouter'
import { useStructuralSharing } from './useMatch'
Expand Down Expand Up @@ -60,7 +60,7 @@ export function useLocation<
}

// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
return useStore(
return useSelector(
router.stores.location,
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
useStructuralSharing(opts, router),
Expand Down
4 changes: 2 additions & 2 deletions packages/react-router/src/useMatch.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'

import * as React from 'react'
import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { invariant, replaceEqualDeep } from '@tanstack/router-core'
import { isServer } from '@tanstack/router-core/isServer'
import { dummyMatchContext, matchContext } from './matchContext'
Expand Down Expand Up @@ -173,7 +173,7 @@ export function useMatch<
useStructuralSharing(opts, router)

// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
const matchSelection = useStore(matchStore, (match) =>
const matchSelection = useSelector(matchStore, (match) =>
match ? selector(match as any) : dummyMatch,
)

Expand Down
4 changes: 2 additions & 2 deletions packages/react-router/src/useRouterState.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { useStore } from '@tanstack/react-store'
import { useSelector } from '@tanstack/react-store'
import { isServer } from '@tanstack/router-core/isServer'
import { useRouter } from './useRouter'
import { useStructuralSharing } from './useMatch'
Expand Down Expand Up @@ -68,7 +68,7 @@ export function useRouterState<
}

// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
return useStore(
return useSelector(
router.stores.__store,
// eslint-disable-next-line react-hooks/rules-of-hooks -- condition is static
useStructuralSharing(opts, router),
Expand Down
2 changes: 1 addition & 1 deletion packages/router-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
"seroval-plugins": "^1.6.2"
},
"devDependencies": {
"@tanstack/store": "^0.9.3",
"@tanstack/store": "^0.11.1",
"@types/node": "25.0.9",
"esbuild": "^0.27.4",
"vite": "*"
Expand Down
2 changes: 1 addition & 1 deletion packages/vue-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
"dependencies": {
"@tanstack/history": "workspace:*",
"@tanstack/router-core": "workspace:*",
"@tanstack/vue-store": "^0.9.3",
"@tanstack/vue-store": "^0.11.1",
"@vue/runtime-dom": "^3.5.25",
"isbot": "^5.1.22"
},
Expand Down
14 changes: 5 additions & 9 deletions packages/vue-router/src/Match.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Vue from 'vue'
import { isNotFound, rootRouteId } from '@tanstack/router-core'
import { isServer } from '@tanstack/router-core/isServer'
import { useStore } from '@tanstack/vue-store'
import { useSelector } from './useSelector'
import { CatchBoundary } from './CatchBoundary'
import { ClientOnly } from './ClientOnly'
import { useRouter } from './useRouter'
Expand All @@ -25,11 +25,7 @@ export const Match = Vue.defineComponent({

const routeId = props.routeId

const activeMatch = useStore(
router.stores.getMatchStore(routeId),
(value) => value,
{ equal: Object.is },
)
const activeMatch = useSelector(router.stores.getMatchStore(routeId))
// Provide routeId context (stable string) for children.
// MatchInner, Outlet, and useMatch all consume this.
Vue.provide(routeIdContext, routeId)
Expand Down Expand Up @@ -142,7 +138,7 @@ export const MatchInner = Vue.defineComponent({

// Use routeId from context (provided by parent Match) — stable string.
const routeId = Vue.inject(routeIdContext)!
const activeMatch = useStore(router.stores.getMatchStore(routeId))
const activeMatch = useSelector(router.stores.getMatchStore(routeId))

// Combined selector for match state AND remount key
// This ensures both are computed in the same selector call with consistent data
Expand Down Expand Up @@ -252,11 +248,11 @@ export const Outlet = Vue.defineComponent({
const router = useRouter()
const parentRouteId = Vue.inject(routeIdContext)!

const parentMatch = useStore(router.stores.getMatchStore(parentRouteId))
const parentMatch = useSelector(router.stores.getMatchStore(parentRouteId))

const route = router.routesById[parentRouteId]!

const childMatch = useStore(router.stores.matches, (matches) => {
const childMatch = useSelector(router.stores.matches, (matches) => {
const index = matches.findIndex(
(match) => match.routeId === parentRouteId,
)
Expand Down
12 changes: 6 additions & 6 deletions packages/vue-router/src/Matches.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Vue from 'vue'
import { isServer } from '@tanstack/router-core/isServer'
import { useStore } from '@tanstack/vue-store'
import { useSelector } from './useSelector'
import { CatchBoundary } from './CatchBoundary'
import { useRouter } from './useRouter'
import { useTransitionerSetup } from './Transitioner'
Expand Down Expand Up @@ -79,7 +79,7 @@ const MatchesInner = Vue.defineComponent({
setup() {
const router = useRouter()

const matches = useStore(router.stores.matches)
const matches = useSelector(router.stores.matches)
const routeId = Vue.computed(() => matches.value[0]?.routeId)

return () => {
Expand Down Expand Up @@ -126,12 +126,12 @@ export type UseMatchRouteOptions<
export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
const router = useRouter()

const location = useStore(router.stores.location, (value) => value.href)
const resolvedLocation = useStore(
const location = useSelector(router.stores.location, (value) => value.href)
const resolvedLocation = useSelector(
router.stores.resolvedLocation,
(value) => value?.href,
)
const status = useStore(router.stores.status)
const status = useSelector(router.stores.status)

return <
const TFrom extends string = string,
Expand Down Expand Up @@ -264,7 +264,7 @@ export function useMatches<
opts?: UseMatchesBaseOptions<TRouter, TSelected>,
): Vue.Ref<UseMatchesResult<TRouter, TSelected>> {
const router = useRouter<TRouter>()
return useStore(router.stores.matches, (matches) => {
return useSelector(router.stores.matches, (matches) => {
return opts?.select
? opts.select(matches as Array<MakeRouteMatchUnion<TRouter>>)
: (matches as any)
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-router/src/Scripts.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Vue from 'vue'
import { _getAssetMatches } from '@tanstack/router-core'
import { useStore } from '@tanstack/vue-store'
import { isServer } from '@tanstack/router-core/isServer'
import { useSelector } from './useSelector'
import { Asset } from './Asset'
import { useRouter } from './useRouter'
import type { RouterManagedTag } from '@tanstack/router-core'
Expand All @@ -18,7 +18,7 @@ export const Scripts = Vue.defineComponent({
setup() {
const router = useRouter()
const nonce = router.options.ssr?.nonce
const matches = useStore(router.stores.matches, _getAssetMatches)
const matches = useSelector(router.stores.matches, _getAssetMatches)

const scripts = Vue.computed(() => {
const userScripts: Array<RouterManagedTag> = []
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-router/src/headContentUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
getScriptPreloadAttrs,
resolveManifestCssLink,
} from '@tanstack/router-core'
import { useStore } from '@tanstack/vue-store'
import { useSelector } from './useSelector'
import { useRouter } from './useRouter'
import type {
AssetCrossOriginConfig,
Expand All @@ -16,7 +16,7 @@ import type {

export const useTags = (assetCrossOrigin?: AssetCrossOriginConfig) => {
const router = useRouter()
const matches = useStore(router.stores.matches, _getAssetMatches)
const matches = useSelector(router.stores.matches, _getAssetMatches)

const tags = Vue.computed<Array<RouterManagedTag>>(() => {
const currentMatches = matches.value
Expand Down
Loading
Loading