Skip to content

Commit

Permalink
fix: action tracking + loaderData cascades
Browse files Browse the repository at this point in the history
  • Loading branch information
tannerlinsley committed Oct 21, 2022
1 parent c7b06d7 commit 98bc989
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 81 deletions.
98 changes: 63 additions & 35 deletions examples/react/kitchen-sink/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
RouterProvider,
createReactRouter,
createRouteConfig,
RouteConfig,
} from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'

Expand All @@ -15,7 +14,6 @@ import {
fetchUsers,
fetchUserById,
Invoice,
User,
postInvoice,
patchInvoice,
} from './mockTodos'
Expand Down Expand Up @@ -51,11 +49,13 @@ const routeConfig = createRouteConfig().addChildren((createRoute) => [
action: async (partialInvoice: Partial<Invoice>) => {
const invoice = await postInvoice(partialInvoice)
// // Redirect to the new invoice
// router.navigate({
// to: invoice.id,
// // Use the current match for relative paths
// from: ctx.match.pathname,
// })
router.navigate({
// from: '/',
to: '/dashboard/invoices/:invoiceId',
params: {
invoiceId: invoice.id,
},
})
return invoice
},
}),
Expand All @@ -72,7 +72,7 @@ const routeConfig = createRouteConfig().addChildren((createRoute) => [
element: <InvoiceView />,
loader: async ({ params: { invoiceId } }) => {
console.log('Fetching invoice...')
const invoice = await fetchInvoiceById(invoiceId!)
const invoice = await fetchInvoiceById(invoiceId)

if (!invoice) {
throw new Error('Invoice not found!')
Expand Down Expand Up @@ -449,11 +449,20 @@ function Invoices() {

// Get the action for a child route
const invoiceIndexRoute = router.useRoute('/dashboard/invoices/')
const invoiceDetailRoute = router.useRoute('/dashboard/invoices/:invoiceId')

return (
<div className="flex-1 flex">
<div className="divide-y w-48">
{invoices?.map((invoice) => {
const foundPending = invoiceDetailRoute.action.pending.find(
(d) => d.submission?.id === invoice.id,
)

if (foundPending?.submission) {
invoice = { ...invoice, ...foundPending.submission }
}

return (
<div key={invoice.id}>
<Link
Expand All @@ -467,15 +476,19 @@ function Invoices() {
>
<pre className="text-sm">
#{invoice.id} - {invoice.title.slice(0, 10)}{' '}
<MatchRoute
to="./:invoiceId"
params={{
invoiceId: invoice.id,
}}
pending
>
{foundPending ? (
<Spinner />
</MatchRoute>
) : (
<MatchRoute
to="./:invoiceId"
params={{
invoiceId: invoice.id,
}}
pending
>
<Spinner />
</MatchRoute>
)}
</pre>
</Link>
</div>
Expand All @@ -499,7 +512,7 @@ function Invoices() {
}

function InvoicesHome() {
const route = router.useMatch('/dashboard/invoices/')
const { action } = router.useMatch('/dashboard/invoices/')

return (
<>
Expand All @@ -509,7 +522,7 @@ function InvoicesHome() {
event.preventDefault()
event.stopPropagation()
const formData = new FormData(event.target as HTMLFormElement)
route.action.submit({
action.submit({
title: formData.get('title') as string,
body: formData.get('body') as string,
})
Expand All @@ -519,10 +532,22 @@ function InvoicesHome() {
<div>Create a new Invoice:</div>
<InvoiceFields invoice={{} as Invoice} />
<div>
<button className="bg-blue-500 rounded p-2 uppercase text-white font-black">
Save
<button
className="bg-blue-500 rounded p-2 uppercase text-white font-black disabled:opacity-50"
disabled={action.current?.status === 'pending'}
>
Create
</button>
</div>
{action.current?.status === 'success' ? (
<div className="inline-block px-2 py-1 rounded bg-green-500 text-white animate-bounce [animation-iteration-count:2.5] [animation-duration:.3s]">
Created!
</div>
) : action.current?.status === 'error' ? (
<div className="inline-block px-2 py-1 rounded bg-red-500 text-white animate-bounce [animation-iteration-count:2.5] [animation-duration:.3s]">
Failed to create.
</div>
) : null}
</form>
</div>
</>
Expand Down Expand Up @@ -550,12 +575,13 @@ function InvoiceView() {

return (
<form
key={invoice?.id}
key={invoice.id}
onSubmit={(event) => {
event.preventDefault()
event.stopPropagation()
const formData = new FormData(event.target as HTMLFormElement)
action.submit({
id: invoice.id,
title: formData.get('title') as string,
body: formData.get('body') as string,
})
Expand All @@ -564,7 +590,7 @@ function InvoiceView() {
>
<InvoiceFields
invoice={invoice}
disabled={action.latest?.status === 'pending'}
disabled={action.current?.status === 'pending'}
/>
<div>
<Link
Expand Down Expand Up @@ -596,23 +622,25 @@ function InvoiceView() {
</div>
<div>
<button
className="bg-blue-500 rounded p-2 uppercase text-white font-black"
disabled={action.latest?.status === 'pending'}
className="bg-blue-500 rounded p-2 uppercase text-white font-black disabled:opacity-50"
disabled={action.current?.status === 'pending'}
>
Save
</button>
</div>
<div key={action.latest?.submittedAt}>
{action.latest?.status === 'success' ? (
<div className="inline-block px-2 py-1 rounded bg-green-500 text-white animate-bounce [animation-iteration-count:2.5] [animation-duration:.3s]">
Saved!
</div>
) : action.latest?.status === 'error' ? (
<div className="inline-block px-2 py-1 rounded bg-red-500 text-white animate-bounce [animation-iteration-count:2.5] [animation-duration:.3s]">
Failed to save.
</div>
) : null}
</div>
{action.current?.submission?.id === invoice.id ? (
<div key={action.current?.submittedAt}>
{action.current?.status === 'success' ? (
<div className="inline-block px-2 py-1 rounded bg-green-500 text-white animate-bounce [animation-iteration-count:2.5] [animation-duration:.3s]">
Saved!
</div>
) : action.current?.status === 'error' ? (
<div className="inline-block px-2 py-1 rounded bg-red-500 text-white animate-bounce [animation-iteration-count:2.5] [animation-duration:.3s]">
Failed to save.
</div>
) : null}
</div>
) : null}
</form>
)
}
Expand Down
13 changes: 10 additions & 3 deletions examples/react/kitchen-sink/src/mockTodos.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PickAsPartial, PickAsRequired } from '@tanstack/react-router'
import { match } from 'assert'
import axios from 'axios'
import { produce } from 'immer'
Expand Down Expand Up @@ -112,17 +113,23 @@ export async function postInvoice(partialInvoice: Partial<Invoice>) {
})
}

export async function patchInvoice(updatedInvoice: Partial<Invoice>) {
export async function patchInvoice({
id,
...updatedInvoice
}: PickAsRequired<Partial<Invoice>, 'id'>) {
return actionDelayFn(() => {
invoices = produce(invoices, (draft) => {
let invoice = draft.find((d) => d.id === updatedInvoice.id)
let invoice = draft.find((d) => d.id === id)
if (!invoice) {
throw new Error('Invoice not found.')
}
if (updatedInvoice.title?.toLocaleLowerCase()?.includes('error')) {
throw new Error('Ouch!')
}
Object.assign(invoice, updatedInvoice)
})

return invoices.find((d) => d.id === updatedInvoice.id)
return invoices.find((d) => d.id === id)
})
}

Expand Down
Loading

0 comments on commit 98bc989

Please sign in to comment.