| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 14160f5 commit 9bfd4f8
95 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -194,25 +194,26 @@ const postsLoader = new Loader({ | |||
| 194 | 194 | }) | |
| 195 | 195 | ||
| 196 | 196 | const loaderClient = new LoaderClient({ | |
| 197 | - getLoaders: () => ({ postsLoader }), | ||
| 197 | + loader: [postsLoader], | ||
| 198 | 198 | }) | |
| 199 | 199 | ||
| 200 | - // Use RootRoute's special `withRouterContext` method to require a specific type | ||
| 201 | - // of router context to be both available in every route and to be passed to | ||
| 202 | - // the router for implementation. | ||
| 200 | + // Create a new routerContext using new RouterContext<{...}>() class and pass it whatever types you would like to be available in your router context. | ||
| 203 | 201 | ||
| 204 | - const rootRoute = RootRoute.withRouterContext<{ | ||
| 202 | + const routerContext = new RouterContext<{ | ||
| 205 | 203 | loaderClient: typeof loaderClient | |
| 206 | - }>()() | ||
| 204 | + }>() | ||
| 205 | + | ||
| 206 | + // Then use the same routerContext to create your root route | ||
| 207 | + const rootRoute = routerContext.createRootRoute() | ||
| 207 | 208 | ||
| 208 | 209 | // Notice how our postsRoute references context to get the loader client | |
| 209 | 210 | // This can be a powerful tool for dependency injection across your router | |
| 210 | 211 | // and routes. | |
| 211 | 212 | const postsRoute = new Route({ | |
| 212 | 213 | getParentPath: () => rootRoute, | |
| 213 | 214 | path: 'posts', | |
| 214 | - async loader({ context }) { | ||
| 215 | - const { postsLoader } = context.loaderClient | ||
| 215 | + async loader({ context: { loaderClient } }) { | ||
| 216 | + const { postsLoader } = loaderClient | ||
| 216 | 217 | await postsLoader.load() | |
| 217 | 218 | return () => useLoader({ loader: postsLoader }) | |
| 218 | 219 | }, | |
@@ -225,10 +226,12 @@ const postsRoute = new Route({ | |||
| 225 | 226 | ||
| 226 | 227 | const routeTree = rootRoute.addChildren([postsRoute]) | |
| 227 | 228 | ||
| 229 | + // Use your routerContext to create a new router | ||
| 230 | + // This will require that you fullfil the type requirements of the routerContext | ||
| 228 | 231 | const router = new Router({ | |
| 229 | 232 | routeTree, | |
| 230 | 233 | context: { | |
| 231 | - // Supply our loaderClient to the whole router | ||
| 234 | + // Supply our loaderClient to the router (and all routes) | ||
| 232 | 235 | loaderClient, | |
| 233 | 236 | }, | |
| 234 | 237 | }) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,7 +15,7 @@ These are just suggested uses of the router context. You can use it for whatever | |||
| 15 | 15 | ||
| 16 | 16 | ## Typed Router Context | |
| 17 | 17 | ||
| 18 | - Like everything else, the router context (at least the one you inject at `new Router()` is strictly typed. This type can be augemented via routes' `getContext` option. If that's the case, the type at the edge of the route is a merged interface-like type of the base context type and every route's `getContext` return type. To constrain the type of the router context, you must use the `RootRoute.withRouterContext()` factory instead of the `new RootRoute()` constructor. Here's an example: | ||
| 18 | + Like everything else, the router context (at least the one you inject at `new Router()` is strictly typed. This type can be augmented via any route's `getContext` option. If that's the case, the type at the edge of the route is a merged interface-like type of the base context type and every route's `getContext` return type. To constrain the type of the root router context, you must use the `new RouteContext<YourContextTypeHere>()` class to create a new `routerContext` and then use the `routerContext.createRootRoute()` method instead of the `new RootRoute()` class to create your root route. Here's an example: | ||
| 19 | 19 | ||
| 20 | 20 | ```tsx | |
| 21 | 21 | import { RootRoute } from '@tanstack/router' | |
@@ -24,12 +24,22 @@ interface MyRouterContext { | |||
| 24 | 24 | user: User | |
| 25 | 25 | } | |
| 26 | 26 | ||
| 27 | - const rootRoute = RootRoute.withRouterContext<MyRouterContext>()({ | ||
| 27 | + const routerContext = new RouterContext<MyRouterContext>() | ||
| 28 | + | ||
| 29 | + // Use the routerContext to create your root route | ||
| 30 | + const rootRoute = routerContext.createRootRoute({ | ||
| 28 | 31 | component: App, | |
| 29 | 32 | }) | |
| 30 | - ``` | ||
| 31 | 33 | ||
| 32 | - > ⚠️ Did you notice the curried call above? Make sure you first call `RootRoute.withRouterContext<MyRouterContext>()` and then call the returned function with the route options. This is a requirement of the `RootRoute.withRouterContext` factory. | ||
| 34 | + const routeTree = rootRoute.addChildren([ | ||
| 35 | + // ... | ||
| 36 | + ]) | ||
| 37 | + | ||
| 38 | + // Use the routerContext to create your router | ||
| 39 | + const router = new Router({ | ||
| 40 | + routeTree, | ||
| 41 | + }) | ||
| 42 | + ``` | ||
| 33 | 43 | ||
| 34 | 44 | ## Passing the initial Router Context | |
| 35 | 45 | ||
@@ -40,6 +50,7 @@ The router context is passed to the router at instantiation time. You can pass t | |||
| 40 | 50 | ```tsx | |
| 41 | 51 | import { Router } from '@tanstack/router' | |
| 42 | 52 | ||
| 53 | + // Use the routerContext you created to create your router | ||
| 43 | 54 | const router = new Router({ | |
| 44 | 55 | routeTree, | |
| 45 | 56 | context: { | |
@@ -58,7 +69,7 @@ Once you have defined the router context type, you can use it in your route defi | |||
| 58 | 69 | ```tsx | |
| 59 | 70 | import { Route } from '@tanstack/router' | |
| 60 | 71 | ||
| 61 | - const userRoute = Route({ | ||
| 72 | + const userRoute = new Route({ | ||
| 62 | 73 | getRootRoute: () => rootRoute, | |
| 63 | 74 | path: 'todos', | |
| 64 | 75 | component: Todos, | |
@@ -68,7 +79,7 @@ const userRoute = Route({ | |||
| 68 | 79 | }) | |
| 69 | 80 | ``` | |
| 70 | 81 | ||
| 71 | - You can even inject your data fetching client itself! | ||
| 82 | + You can even inject your data fetching client itself... in fact, this is highly recommended! | ||
| 72 | 83 | ||
| 73 | 84 | ```tsx | |
| 74 | 85 | import { RootRoute } from '@tanstack/router' | |
@@ -77,9 +88,7 @@ interface MyRouterContext { | |||
| 77 | 88 | queryClient: QueryClient | |
| 78 | 89 | } | |
| 79 | 90 | ||
| 80 | - const rootRoute = RootRoute.withRouterContext<MyRouterContext>()({ | ||
| 81 | - component: App, | ||
| 82 | - }) | ||
| 91 | + const routerContext = new RouterContext<MyRouterContext>() | ||
| 83 | 92 | ||
| 84 | 93 | const queryClient = new QueryClient() | |
| 85 | 94 | ||
@@ -96,7 +105,7 @@ Then, in your route: | |||
| 96 | 105 | ```tsx | |
| 97 | 106 | import { Route } from '@tanstack/router' | |
| 98 | 107 | ||
| 99 | - const userRoute = Route({ | ||
| 108 | + const userRoute = new Route({ | ||
| 100 | 109 | getRootRoute: () => rootRoute, | |
| 101 | 110 | path: 'todos', | |
| 102 | 111 | component: Todos, | |
@@ -120,7 +129,9 @@ interface MyRouterContext { | |||
| 120 | 129 | foo: boolean | |
| 121 | 130 | } | |
| 122 | 131 | ||
| 123 | - const rootRoute = RootRoute.withRouterContext<MyRouterContext>()({ | ||
| 132 | + const routerContext = new RouterContext<MyRouterContext>() | ||
| 133 | + | ||
| 134 | + const rootRoute = routerContext.createRootRoute({ | ||
| 124 | 135 | component: App, | |
| 125 | 136 | }) | |
| 126 | 137 | ||
@@ -131,7 +142,7 @@ const router = new Router({ | |||
| 131 | 142 | }, | |
| 132 | 143 | }) | |
| 133 | 144 | ||
| 134 | - const userRoute = Route({ | ||
| 145 | + const userRoute = new Route({ | ||
| 135 | 146 | getRootRoute: () => rootRoute, | |
| 136 | 147 | path: 'admin', | |
| 137 | 148 | component: Todos, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -216,7 +216,7 @@ export function createRouter() { | |||
| 216 | 216 | // Optionally, we can use `Wrap` to wrap our router in the loader client provider | |
| 217 | 217 | Wrap: ({ children }) => { | |
| 218 | 218 | return ( | |
| 219 | - <LoaderClientProvider loaderClient={loaderClient}> | ||
| 219 | + <LoaderClientProvider client={loaderClient}> | ||
| 220 | 220 | {children} | |
| 221 | 221 | </LoaderClientProvider> | |
| 222 | 222 | ) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,15 +62,13 @@ The `from` property is optional, which means if you don't pass it, you'll get th | |||
| 62 | 62 | ||
| 63 | 63 | Router context is so extremely useful as it's the ultimate hierarchical dependency injection. You can supply context to the router and to each and every route it renders. As you build up this context, TanStack Router will merge it down with the hierarchy of routes, so that each route has access to the context of all of its parents. | |
| 64 | 64 | ||
| 65 | - If you want to use context, it's highly recommended that you use the `RootRoute.withRouterContext<ContextType>()(rootRouteOptions)` utility. | ||
| 66 | - | ||
| 67 | - This utility will create a requirement for you to pass a context type to your router, and will also ensure that your context is properly typed throughout the entire route tree. | ||
| 65 | + The `new RouteContext()` utility creates a new router context that when instantiated with a type, creates a requirement for you to fullfil the same type contract to your router, and will also ensure that your context is properly typed throughout the entire route tree. | ||
| 68 | 66 | ||
| 69 | 67 | ```tsx | |
| 70 | - const rootRoute = new RootRoute.withRouterContext<{ whateverYouWant: true }>()({ | ||
| 71 | - component: () => { | ||
| 72 | - // ... | ||
| 73 | - }, | ||
| 68 | + const routeContext = new RouteContext<{ whateverYouWant: true }>() | ||
| 69 | + | ||
| 70 | + const rootRoute = routeContext.createRootRoute({ | ||
| 71 | + component: App, | ||
| 74 | 72 | }) | |
| 75 | 73 | ||
| 76 | 74 | const routeTree = rootRoute.addChildren([ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,7 +12,6 @@ import express from 'express' | |||
| 12 | 12 | // index.js | |
| 13 | 13 | import './fetch-polyfill' | |
| 14 | 14 | import { createRouter } from './router' | |
| 15 | - import { Transform } from 'stream' | ||
| 16 | 15 | ||
| 17 | 16 | type ReactReadableStream = ReadableStream<Uint8Array> & { | |
| 18 | 17 | allReady?: Promise<void> | undefined | |
@@ -34,7 +33,6 @@ export async function render(opts: { | |||
| 34 | 33 | router.update({ | |
| 35 | 34 | history: memoryHistory, | |
| 36 | 35 | context: { | |
| 37 | - ...router.context, | ||
| 38 | 36 | head: opts.head, | |
| 39 | 37 | }, | |
| 40 | 38 | }) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -30,7 +30,7 @@ export function App({ | |||
| 30 | 30 | /> | |
| 31 | 31 | </head> | |
| 32 | 32 | <body> | |
| 33 | - <LoaderClientProvider loaderClient={loaderClient}> | ||
| 33 | + <LoaderClientProvider client={loaderClient}> | ||
| 34 | 34 | <RouterProvider router={router} /> | |
| 35 | 35 | </LoaderClientProvider> | |
| 36 | 36 | </body> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4,7 +4,7 @@ import { postLoader } from './routes/posts/$postId' | |||
| 4 | 4 | ||
| 5 | 5 | export const createLoaderClient = () => { | |
| 6 | 6 | return new LoaderClient({ | |
| 7 | - getLoaders: () => ({ postsLoader, postLoader, testLoader }), | ||
| 7 | + loaders: [postsLoader, postLoader, testLoader], | ||
| 8 | 8 | }) | |
| 9 | 9 | } | |
| 10 | 10 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,4 @@ | |||
| 1 | - import { Router } from '@tanstack/router' | ||
| 1 | + import { Router, RouterContext } from '@tanstack/router' | ||
| 2 | 2 | import { LoaderClientProvider } from '@tanstack/react-loaders' | |
| 3 | 3 | ||
| 4 | 4 | import { rootRoute } from './routes/root' | |
@@ -10,10 +10,10 @@ import { postIdRoute } from './routes/posts/$postId' | |||
| 10 | 10 | import { createLoaderClient } from './loaderClient' | |
| 11 | 11 | import React from 'react' | |
| 12 | 12 | ||
| 13 | - export type RouterContext = { | ||
| 13 | + export const routerContext = new RouterContext<{ | ||
| 14 | 14 | loaderClient: ReturnType<typeof createLoaderClient> | |
| 15 | 15 | head: string | |
| 16 | - } | ||
| 16 | + }>() | ||
| 17 | 17 | ||
| 18 | 18 | export const routeTree = rootRoute.addChildren([ | |
| 19 | 19 | indexRoute, | |
@@ -42,7 +42,7 @@ export function createRouter() { | |||
| 42 | 42 | // Wrap our router in the loader client provider | |
| 43 | 43 | Wrap: ({ children }) => { | |
| 44 | 44 | return ( | |
| 45 | - <LoaderClientProvider loaderClient={loaderClient}> | ||
| 45 | + <LoaderClientProvider client={loaderClient}> | ||
| 46 | 46 | {children} | |
| 47 | 47 | </LoaderClientProvider> | |
| 48 | 48 | ) | |
@@ -55,7 +55,7 @@ export function createRouter() { | |||
| 55 | 55 | hydrateLoaderInstanceFn: (instance) => | |
| 56 | 56 | router.hydrateData(instance.hashedKey) as any, | |
| 57 | 57 | dehydrateLoaderInstanceFn: (instance) => | |
| 58 | - router.dehydrateData(instance.hashedKey, () => instance.state), | ||
| 58 | + router.dehydrateData(instance.hashedKey, () => instance), | ||
| 59 | 59 | } | |
| 60 | 60 | ||
| 61 | 61 | return router | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,30 +1,17 @@ | |||
| 1 | 1 | import * as React from 'react' | |
| 2 | - import { | ||
| 3 | - Link, | ||
| 4 | - Outlet, | ||
| 5 | - Route, | ||
| 6 | - StreamedPromise, | ||
| 7 | - useDehydrate, | ||
| 8 | - useHydrate, | ||
| 9 | - useInjectHtml, | ||
| 10 | - useRouter, | ||
| 11 | - } from '@tanstack/router' | ||
| 2 | + import { Link, Outlet, Route } from '@tanstack/router' | ||
| 12 | 3 | import { rootRoute } from './root' | |
| 13 | - // import { loaderClient } from '../entry-client' | ||
| 14 | - import { Loader } from '@tanstack/react-loaders' | ||
| 4 | + import { Loader, useLoaderInstance } from '@tanstack/react-loaders' | ||
| 15 | 5 | import { postIdRoute } from './posts/$postId' | |
| 16 | 6 | ||
| 17 | - declare module 'react' { | ||
| 18 | - function use<T>(promise: Promise<T>): T | ||
| 19 | - } | ||
| 20 | - | ||
| 21 | 7 | export type PostType = { | |
| 22 | 8 | id: string | |
| 23 | 9 | title: string | |
| 24 | 10 | body: string | |
| 25 | 11 | } | |
| 26 | 12 | ||
| 27 | 13 | export const postsLoader = new Loader({ | |
| 14 | + key: 'posts', | ||
| 28 | 15 | fn: async () => { | |
| 29 | 16 | console.log('Fetching posts...') | |
| 30 | 17 | await new Promise((r) => | |
@@ -38,6 +25,7 @@ export const postsLoader = new Loader({ | |||
| 38 | 25 | }) | |
| 39 | 26 | ||
| 40 | 27 | export const testLoader = new Loader({ | |
| 28 | + key: 'test', | ||
| 41 | 29 | fn: async (wait: number) => { | |
| 42 | 30 | await new Promise((r) => setTimeout(r, wait)) | |
| 43 | 31 | return { | |
@@ -49,27 +37,20 @@ export const testLoader = new Loader({ | |||
| 49 | 37 | export const postsRoute = new Route({ | |
| 50 | 38 | getParentRoute: () => rootRoute, | |
| 51 | 39 | path: 'posts', | |
| 52 | - loader: async ({ context, preload }) => { | ||
| 53 | - const { postsLoader } = context.loaderClient.loaders | ||
| 54 | - await postsLoader.load({ preload }) | ||
| 55 | - return { | ||
| 56 | - usePosts: () => postsLoader.useLoader(), | ||
| 57 | - } | ||
| 40 | + loader: async ({ context: { loaderClient }, preload }) => { | ||
| 41 | + await loaderClient.load({ key: 'posts', preload }) | ||
| 42 | + return () => useLoaderInstance({ key: 'posts' }) | ||
| 58 | 43 | }, | |
| 59 | 44 | component: function Posts({ useLoader }) { | |
| 60 | - const { usePosts } = useLoader() | ||
| 61 | - | ||
| 62 | - const { | ||
| 63 | - state: { data: posts }, | ||
| 64 | - } = usePosts() | ||
| 45 | + const { data: posts } = useLoader()() | ||
| 65 | 46 | ||
| 66 | 47 | return ( | |
| 67 | 48 | <div className="p-2 flex gap-2"> | |
| 68 | 49 | <Test wait={1000 / 2} /> | |
| 69 | - <Test wait={2000 / 2} /> | ||
| 70 | 50 | <Test wait={3000 / 2} /> | |
| 71 | - <Test wait={4000 / 2} /> | ||
| 51 | + <Test wait={2000 / 2} /> | ||
| 72 | 52 | <Test wait={5000 / 2} /> | |
| 53 | + <Test wait={4000 / 2} /> | ||
| 73 | 54 | <ul className="list-disc pl-4"> | |
| 74 | 55 | {posts?.map((post) => { | |
| 75 | 56 | return ( | |
@@ -104,9 +85,10 @@ function Test({ wait }: { wait: number }) { | |||
| 104 | 85 | } | |
| 105 | 86 | ||
| 106 | 87 | function TestInner({ wait }: { wait: number }) { | |
| 107 | - const instance = testLoader.useLoader({ | ||
| 88 | + const instance = useLoaderInstance({ | ||
| 89 | + key: 'test', | ||
| 108 | 90 | variables: wait, | |
| 109 | 91 | }) | |
| 110 | 92 | ||
| 111 | - return <div>Test: {instance.state.data.test}</div> | ||
| 93 | + return <div>Test: {instance.data.test}</div> | ||
| 112 | 94 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments