Skip to content

Commit

Permalink
fix: big refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
tannerlinsley committed Jul 28, 2023
1 parent 14160f5 commit 9bfd4f8
Show file tree
Hide file tree
Showing 95 changed files with 2,618 additions and 2,351 deletions.
21 changes: 12 additions & 9 deletions docs/guide/data-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,25 +194,26 @@ const postsLoader = new Loader({
})

const loaderClient = new LoaderClient({
getLoaders: () => ({ postsLoader }),
loader: [postsLoader],
})

// Use RootRoute's special `withRouterContext` method to require a specific type
// of router context to be both available in every route and to be passed to
// the router for implementation.
// Create a new routerContext using new RouterContext<{...}>() class and pass it whatever types you would like to be available in your router context.

const rootRoute = RootRoute.withRouterContext<{
const routerContext = new RouterContext<{
loaderClient: typeof loaderClient
}>()()
}>()

// Then use the same routerContext to create your root route
const rootRoute = routerContext.createRootRoute()

// Notice how our postsRoute references context to get the loader client
// This can be a powerful tool for dependency injection across your router
// and routes.
const postsRoute = new Route({
getParentPath: () => rootRoute,
path: 'posts',
async loader({ context }) {
const { postsLoader } = context.loaderClient
async loader({ context: { loaderClient } }) {
const { postsLoader } = loaderClient
await postsLoader.load()
return () => useLoader({ loader: postsLoader })
},
Expand All @@ -225,10 +226,12 @@ const postsRoute = new Route({

const routeTree = rootRoute.addChildren([postsRoute])

// Use your routerContext to create a new router
// This will require that you fullfil the type requirements of the routerContext
const router = new Router({
routeTree,
context: {
// Supply our loaderClient to the whole router
// Supply our loaderClient to the router (and all routes)
loaderClient,
},
})
Expand Down
35 changes: 23 additions & 12 deletions docs/guide/router-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ These are just suggested uses of the router context. You can use it for whatever

## Typed Router Context

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:
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:

```tsx
import { RootRoute } from '@tanstack/router'
Expand All @@ -24,12 +24,22 @@ interface MyRouterContext {
user: User
}

const rootRoute = RootRoute.withRouterContext<MyRouterContext>()({
const routerContext = new RouterContext<MyRouterContext>()

// Use the routerContext to create your root route
const rootRoute = routerContext.createRootRoute({
component: App,
})
```

> ⚠️ 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.
const routeTree = rootRoute.addChildren([
// ...
])

// Use the routerContext to create your router
const router = new Router({
routeTree,
})
```

## Passing the initial Router Context

Expand All @@ -40,6 +50,7 @@ The router context is passed to the router at instantiation time. You can pass t
```tsx
import { Router } from '@tanstack/router'

// Use the routerContext you created to create your router
const router = new Router({
routeTree,
context: {
Expand All @@ -58,7 +69,7 @@ Once you have defined the router context type, you can use it in your route defi
```tsx
import { Route } from '@tanstack/router'

const userRoute = Route({
const userRoute = new Route({
getRootRoute: () => rootRoute,
path: 'todos',
component: Todos,
Expand All @@ -68,7 +79,7 @@ const userRoute = Route({
})
```

You can even inject your data fetching client itself!
You can even inject your data fetching client itself... in fact, this is highly recommended!

```tsx
import { RootRoute } from '@tanstack/router'
Expand All @@ -77,9 +88,7 @@ interface MyRouterContext {
queryClient: QueryClient
}

const rootRoute = RootRoute.withRouterContext<MyRouterContext>()({
component: App,
})
const routerContext = new RouterContext<MyRouterContext>()

const queryClient = new QueryClient()

Expand All @@ -96,7 +105,7 @@ Then, in your route:
```tsx
import { Route } from '@tanstack/router'

const userRoute = Route({
const userRoute = new Route({
getRootRoute: () => rootRoute,
path: 'todos',
component: Todos,
Expand All @@ -120,7 +129,9 @@ interface MyRouterContext {
foo: boolean
}

const rootRoute = RootRoute.withRouterContext<MyRouterContext>()({
const routerContext = new RouterContext<MyRouterContext>()

const rootRoute = routerContext.createRootRoute({
component: App,
})

Expand All @@ -131,7 +142,7 @@ const router = new Router({
},
})

const userRoute = Route({
const userRoute = new Route({
getRootRoute: () => rootRoute,
path: 'admin',
component: Todos,
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/ssr-and-streaming.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export function createRouter() {
// Optionally, we can use `Wrap` to wrap our router in the loader client provider
Wrap: ({ children }) => {
return (
<LoaderClientProvider loaderClient={loaderClient}>
<LoaderClientProvider client={loaderClient}>
{children}
</LoaderClientProvider>
)
Expand Down
12 changes: 5 additions & 7 deletions docs/guide/type-safety.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,13 @@ The `from` property is optional, which means if you don't pass it, you'll get th

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.

If you want to use context, it's highly recommended that you use the `RootRoute.withRouterContext<ContextType>()(rootRouteOptions)` utility.

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.
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.

```tsx
const rootRoute = new RootRoute.withRouterContext<{ whateverYouWant: true }>()({
component: () => {
// ...
},
const routeContext = new RouteContext<{ whateverYouWant: true }>()

const rootRoute = routeContext.createRootRoute({
component: App,
})

const routeTree = rootRoute.addChildren([
Expand Down
2 changes: 0 additions & 2 deletions examples/react/basic-ssr-streaming/src/entry-server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import express from 'express'
// index.js
import './fetch-polyfill'
import { createRouter } from './router'
import { Transform } from 'stream'

type ReactReadableStream = ReadableStream<Uint8Array> & {
allReady?: Promise<void> | undefined
Expand All @@ -34,7 +33,6 @@ export async function render(opts: {
router.update({
history: memoryHistory,
context: {
...router.context,
head: opts.head,
},
})
Expand Down
2 changes: 1 addition & 1 deletion examples/react/basic-ssr-streaming/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function App({
/>
</head>
<body>
<LoaderClientProvider loaderClient={loaderClient}>
<LoaderClientProvider client={loaderClient}>
<RouterProvider router={router} />
</LoaderClientProvider>
</body>
Expand Down
2 changes: 1 addition & 1 deletion examples/react/basic-ssr-streaming/src/loaderClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { postLoader } from './routes/posts/$postId'

export const createLoaderClient = () => {
return new LoaderClient({
getLoaders: () => ({ postsLoader, postLoader, testLoader }),
loaders: [postsLoader, postLoader, testLoader],
})
}

Expand Down
10 changes: 5 additions & 5 deletions examples/react/basic-ssr-streaming/src/router.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Router } from '@tanstack/router'
import { Router, RouterContext } from '@tanstack/router'
import { LoaderClientProvider } from '@tanstack/react-loaders'

import { rootRoute } from './routes/root'
Expand All @@ -10,10 +10,10 @@ import { postIdRoute } from './routes/posts/$postId'
import { createLoaderClient } from './loaderClient'
import React from 'react'

export type RouterContext = {
export const routerContext = new RouterContext<{
loaderClient: ReturnType<typeof createLoaderClient>
head: string
}
}>()

export const routeTree = rootRoute.addChildren([
indexRoute,
Expand Down Expand Up @@ -42,7 +42,7 @@ export function createRouter() {
// Wrap our router in the loader client provider
Wrap: ({ children }) => {
return (
<LoaderClientProvider loaderClient={loaderClient}>
<LoaderClientProvider client={loaderClient}>
{children}
</LoaderClientProvider>
)
Expand All @@ -55,7 +55,7 @@ export function createRouter() {
hydrateLoaderInstanceFn: (instance) =>
router.hydrateData(instance.hashedKey) as any,
dehydrateLoaderInstanceFn: (instance) =>
router.dehydrateData(instance.hashedKey, () => instance.state),
router.dehydrateData(instance.hashedKey, () => instance),
}

return router
Expand Down
44 changes: 13 additions & 31 deletions examples/react/basic-ssr-streaming/src/routes/posts.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
import * as React from 'react'
import {
Link,
Outlet,
Route,
StreamedPromise,
useDehydrate,
useHydrate,
useInjectHtml,
useRouter,
} from '@tanstack/router'
import { Link, Outlet, Route } from '@tanstack/router'
import { rootRoute } from './root'
// import { loaderClient } from '../entry-client'
import { Loader } from '@tanstack/react-loaders'
import { Loader, useLoaderInstance } from '@tanstack/react-loaders'
import { postIdRoute } from './posts/$postId'

declare module 'react' {
function use<T>(promise: Promise<T>): T
}

export type PostType = {
id: string
title: string
body: string
}

export const postsLoader = new Loader({
key: 'posts',
fn: async () => {
console.log('Fetching posts...')
await new Promise((r) =>
Expand All @@ -38,6 +25,7 @@ export const postsLoader = new Loader({
})

export const testLoader = new Loader({
key: 'test',
fn: async (wait: number) => {
await new Promise((r) => setTimeout(r, wait))
return {
Expand All @@ -49,27 +37,20 @@ export const testLoader = new Loader({
export const postsRoute = new Route({
getParentRoute: () => rootRoute,
path: 'posts',
loader: async ({ context, preload }) => {
const { postsLoader } = context.loaderClient.loaders
await postsLoader.load({ preload })
return {
usePosts: () => postsLoader.useLoader(),
}
loader: async ({ context: { loaderClient }, preload }) => {
await loaderClient.load({ key: 'posts', preload })
return () => useLoaderInstance({ key: 'posts' })
},
component: function Posts({ useLoader }) {
const { usePosts } = useLoader()

const {
state: { data: posts },
} = usePosts()
const { data: posts } = useLoader()()

return (
<div className="p-2 flex gap-2">
<Test wait={1000 / 2} />
<Test wait={2000 / 2} />
<Test wait={3000 / 2} />
<Test wait={4000 / 2} />
<Test wait={2000 / 2} />
<Test wait={5000 / 2} />
<Test wait={4000 / 2} />
<ul className="list-disc pl-4">
{posts?.map((post) => {
return (
Expand Down Expand Up @@ -104,9 +85,10 @@ function Test({ wait }: { wait: number }) {
}

function TestInner({ wait }: { wait: number }) {
const instance = testLoader.useLoader({
const instance = useLoaderInstance({
key: 'test',
variables: wait,
})

return <div>Test: {instance.state.data.test}</div>
return <div>Test: {instance.data.test}</div>
}
Loading

0 comments on commit 9bfd4f8

Please sign in to comment.