Search code examples
typescriptreact-contexttanstack-router

Tanstack Router - How to pass value from another context


I'm building a react app that is using Tanstack router, and I'd like to access values from a context provider that I created over to the router beforeLoad handler for a given route. My app structure looks like this:

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <AppProvider>
      <RouteProvider />
    </AppProvider>
  </React.StrictMode>
);

And the RouteProvider component definition is this:

import { RouterProvider as TSRouteProvider } from '@tanstack/react-router';
...

const RouteProvider: FC = () => {
  const { user, signedIn, loaded } = useApp();

  if (!loaded) {
     return null;
  }

  return <TSRouteProvider router={router} context={{ user, signedIn }} />;
};

export default RouteProvider;

You can see above that I'm trying to use the context prop to pass values from my custom app context hook into the Tanstack router, however the tanstack router context never seems to receive the updated values. Is there a way to do this?


Solution

  • TLDR use router.invalidate() when the context data changes.

    The RouteProvider is not a real React Context, so it is basically static, however you can invalidate it, thus triggering a refresh.

    Note this refreshes all the router caches, including beforeLoad and loader, so they will have to reload data again. Therefore don't put too much in the RouterContext as you want to only call invalidate when you really have to, such as user logout etc.

    A good way to handle this is with a single object behind a useMemo:

    const routerContext = useMemo(() => {
        return {
            user,
            signedIn
        };
    }, [
        user,
        signedIn
    ]);
    

    Now with useEffect you then invalidate the routerContext anytime it changes, now the routerContext should work in your beforeLoad and loader functions.

    useEffect(() => {
        router.invalidate();
    }, [routerContext]);
    

    And pass it in to RouterProvider:

    return [...] <RouterProvider router={router} context={routerContext} />
    

    It's insane that this is buried in the Tanstack Example here: https://tanstack.com/router/latest/docs/framework/react/examples/kitchen-sink-react-query-file-based, but not mentioned anywhere in the Tanstack docs for Router Context right now: https://tanstack.com/router/v1/docs/framework/react/guide/router-context.