remix
remix copied to clipboard
feat: the real implementation of deferred
🚨 IMPORTANT: Revert any changes to version
and release
scripts before merging 🚨
🦋 Changeset detected
Latest commit: cbec20ee92cad1bb0e2eebff8bbf3c20454dd2c8
The changes in this PR will be included in the next version bump.
This PR includes changesets to release 17 packages
Name | Type |
---|---|
@remix-run/cloudflare | Major |
@remix-run/deferred | Major |
@remix-run/deno | Major |
@remix-run/react | Major |
@remix-run/server-runtime | Major |
@remix-run/cloudflare-pages | Major |
@remix-run/cloudflare-workers | Major |
@remix-run/dev | Major |
@remix-run/node | Major |
create-remix | Major |
@remix-run/architect | Major |
@remix-run/express | Major |
@remix-run/netlify | Major |
@remix-run/vercel | Major |
@remix-run/serve | Major |
remix | Major |
@remix-run/eslint-config | Major |
Not sure what this means? Click here to learn what changesets are.
Click here if you're a maintainer who wants to add another changeset to this PR
Could you double-check the highlighted lines?
Just went through them and they look good to me.
can anyone share the status of the deferred API? when will this PR be merged ?
It will be part of "Remixing React Router" where they port Remix on top of React Router 6.4 (which was just released). They are working on the port now. I'm not sure of the timeline. If you want to try out the features, you can install the remix packages using the deferred version tag.
https://remix.run/blog/remixing-react-router
How to access the error reason if the defer value rejected, like inside the errorElement? is there a useAsyncError
hook?
@lili21 Yep! I would refer to the React Router docs for now as the eventual stable Remix API should match: https://reactrouter.com/en/main/guides/deferred and https://reactrouter.com/en/main/components/await
Heya 👋
I found a TypeScript bug. In a situation where you want to A/B test deferring some data, the render prop doesn't seem to know what to do with the type. Here's my loader:
export async function loader({ request, params }: LoaderArgs) {
const user = await requireUser(request);
const { customerId } = params;
invariant(customerId, "customerId param is required");
const customerInfo = await getCustomerInfo(customerId);
if (!customerInfo) {
throw new Response("not found", { status: 404 });
}
const invoiceDetailsPromise = getCustomerInvoiceDetails(customerId);
const deferInvoices = await shouldDeferInvoices({
customerId,
userId: user.id,
});
return defer({
customerInfo,
invoiceDetails: deferInvoices
? invoiceDetailsPromise
: await invoiceDetailsPromise,
});
}
And here's the Await
usage:
<Suspense fallback={<InvoiceDetailsFallback />}>
<Await
resolve={data.invoiceDetails}
errorElement={
<div className="relative h-full">
<ErrorFallback />
</div>
}
>
{(invoiceDetails) => (
<table className="w-full">
<tbody>
{/* @ts-expect-error This needs to be fixed in Remix */}
{invoiceDetails.map((details) => (
<Etc />
))}
</tbody>
</table>
)}
</Await>
</Suspense>
The TypeScript error I get on invoiceDetails
is:
Property 'map' does not exist on type 'SerializeObject<{ totalAmount: number; totalDeposits: number; daysToDueDate: number; dueStatus: DueStatus; dueStatusDisplay: string; id: string; number: number; }>[] | SerializeObject<...>'.
Property 'map' does not exist on type 'SerializeObject<{ then: <TResult1 = { totalAmount: number; totalDeposits: number; daysToDueDate: number; dueStatus: DueStatus; dueStatusDisplay: string; id: string; number: number; }[], TResult2 = never>(onfulfilled?: ((value: { totalAmount: number; ... 5 more ...; number: number; }[]) => TResult1 | PromiseLike<...>...'.
Interestingly, if I change my loader for invoiceDetails
to either only await or only not await, then it works fine. It's just not jazzed about using both.
Here's the spot in my repo: https://github.com/kentcdodds/stream-away-the-wait-talk/blob/2526cafd0a758c20c3ef70ba78b008bacabd19bc/apps/11-levers/app/routes/__app/sales/customers/%24customerId.tsx#L70
Feel free to reproduce via:
npx create-remix ./defer-types-issue-repro --template=https://github.com/kentcdodds/stream-away-the-wait-talk/tree/main/apps/11-levers --typescript --install
cd ./defer-types-issue-repro
open "app/routes/__app/sales/customers/\$customerId.tsx"
# delete the ts-expect-error on line 70
npm run typecheck
does it work inside worker runtime, like cloudflare worker?
Reporting an error I'm seeing with a page using defer
to stream my page.
Stackblitz reproducing the error (same behavior happens on my Mac):
- https://stackblitz.com/edit/node-whsnyv
I am using the deferred
tag & package.json looks like this:
{
"private": true,
"sideEffects": false,
"scripts": {
"build": "remix build",
"dev": "remix dev",
"start": "remix-serve build"
},
"dependencies": {
"@remix-run/node": "deferred",
"@remix-run/react": "deferred",
"@remix-run/serve": "deferred",
"isbot": "^3.5.4",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@remix-run/dev": "deferred",
"@remix-run/eslint-config": "deferred",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"eslint": "^8.23.1",
"typescript": "^4.7.4"
},
"engines": {
"node": ">=14"
}
}
Steps to Reproduce
- Load route which is using
defer
- Refresh the route which is using
defer
and you'll see the 2nd time it no longer works (see terminal error) and the page hangs Stackblitz reproducing the error: https://stackblitz.com/edit/node-whsnyv
Error: The render was aborted by the server without a reason.
at abortTask (/Users/cfajardo/Repos/remix-ssr-vs-ssr-streaming/node_modules/react-dom/cjs/react-dom-server.node.development.js:6436:43)
at /Users/cfajardo/Repos/remix-ssr-vs-ssr-streaming/node_modules/react-dom/cjs/react-dom-server.node.development.js:7002:14
at Set.forEach (<anonymous>)
at abort (/Users/cfajardo/Repos/remix-ssr-vs-ssr-streaming/node_modules/react-dom/cjs/react-dom-server.node.development.js:7001:20)
at Timeout.abort [as _onTimeout] (/Users/cfajardo/Repos/remix-ssr-vs-ssr-streaming/node_modules/react-dom/cjs/react-dom-server.node.development.js:7051:7)
at listOnTimeout (node:internal/timers:559:17)
at processTimers (node:internal/timers:502:7)
Video Reproduction:
https://user-images.githubusercontent.com/6743796/200146863-71ac5799-eb70-44c2-8137-5ff6f4db2d77.mp4
does it work inside worker runtime, like cloudflare worker?
✘ [ERROR] Could not resolve "fs/promises"
when I'm trying to use defer
in the cloudflare worker template.
@cliffordfajardo try decrease the getPeopleDelayTime
value. or increase the ABORT_DELAY
value inside entry.server.tsx
.
if the deferred promise reject, it will throw the unhandlePromiseRejection
error, which will break the application by default.
It was a good run 😅
For anyone looking for the new PR: https://github.com/remix-run/react-router/pull/9760