'use client' import { useStore } from '@tanstack/react-store' import { useRef } from 'react' import { replaceEqualDeep } from '@tanstack/router-core' import { isServer } from '@tanstack/router-core/isServer' import { useRouter } from './useRouter' import type { AnyRouter, RegisteredRouter, RouterState, } from '@tanstack/router-core' import type { StructuralSharingOption, ValidateSelected, } from './structuralSharing' export type UseRouterStateOptions< TRouter extends AnyRouter, TSelected, TStructuralSharing, > = { router?: TRouter select?: ( state: RouterState, ) => ValidateSelected } & StructuralSharingOption export type UseRouterStateResult< TRouter extends AnyRouter, TSelected, > = unknown extends TSelected ? RouterState : TSelected /** * Subscribe to the router's state store with optional selection and * structural sharing for render optimization. * * Options: * - `select`: Project the full router state to a derived slice * - `structuralSharing`: Replace-equal semantics for stable references * - `router`: Read state from a specific router instance instead of context * * @returns The selected router state (or the full state by default). * @link https://tanstack.com/router/latest/docs/framework/react/api/router/useRouterStateHook */ export function useRouterState< TRouter extends AnyRouter = RegisteredRouter, TSelected = unknown, TStructuralSharing extends boolean = boolean, >( opts?: UseRouterStateOptions, ): UseRouterStateResult { const contextRouter = useRouter({ warn: opts?.router === undefined, }) const router = opts?.router || contextRouter // During SSR we render exactly once and do not need reactivity. // Avoid subscribing to the store (and any structural sharing work) on the server. const _isServer = isServer ?? router.isServer if (_isServer) { const state = router.stores.__store.get() as RouterState< TRouter['routeTree'] > return (opts?.select ? opts.select(state) : state) as UseRouterStateResult< TRouter, TSelected > } const previousResult = // eslint-disable-next-line react-hooks/rules-of-hooks useRef>(undefined) // eslint-disable-next-line react-hooks/rules-of-hooks return useStore(router.stores.__store, (state) => { if (opts?.select) { if (opts.structuralSharing ?? router.options.defaultStructuralSharing) { const newSlice = replaceEqualDeep( previousResult.current, opts.select(state), ) previousResult.current = newSlice return newSlice } return opts.select(state) } return state }) as UseRouterStateResult }