remix
remix copied to clipboard
Deferred loader routes crash remix server on refresh
What version of Remix are you using?
@remix-run/node:0.0.0-experimental-9b7f37c9a, @remix-run/react:0.0.0-experimental-9b7f37c9a
Steps to Reproduce
I am using node v18.12.0, also tried using node v16.2.0. Using WSL 2 on Windows 11.
https://github.com/cephalization/remix-house-stack/issues/9
- Clone template
npx create-remix@latest --template cephalization/remix-house-stack npm installthe root of the projectnpm run dev- Express API and Remix Dev Server will launch
- Navigate to localhost:3000
- Two things will happen;
- A non-deferred request to localhost:5001/healthz that completes successfully
- A deferred request to localhost:5001/delayedhealth that completes after 5 seconds
- Now make a change to UI code or refresh the page
- The deferred request now fails and the dev server outputs
Error: The render was aborted by the server without a reason.
Expected Behavior
Refreshing the page or live reloading code changes does not abort renders.
Actual Behavior
Refreshing the page or live reloading code changes aborts renders and generates error messages.
Can you upgrade to 0.0.0-experimental-2f4891673 and give it a go?
I cloned the repo and gave your reproduction steps a go without issue.
The error is still logged but does not bring down your server. This error occurs from within react when the streaming renderer is aborted (in this case by the connection being closed by the client) before all suspended trees resolves.
Is this something that needs fixing in my client code? I am able to confirm that it no longer crashes the remix server but the streaming request never recovers after the first refresh despite getting 200 responses from the API serving the deferred data.
I also see this happening in my app as well.
It seems if I just refresh a page using defer many times, I eventually get this error to pop up.
That makes sense, because the client disconnected.
This is what doesn't make sense:
await new Promise((resolve) => setTimeout(resolve, 10000));
Adding this to a deffered promise. Every time, it says The render was aborted by the server without a reason
it's like React will not stream if more than 10s has passed even if the client is still waiting
I can confirm this error from Chrome using Remix 1.11.1:
2react-dom.development.js:20662 Uncaught Error: The server did not finish this Suspense boundary: The render was aborted by the server without a reason.
at updateDehydratedSuspenseComponent (react-dom.development.js:20662:17)
at updateSuspenseComponent (react-dom.development.js:20362:16)
at beginWork (react-dom.development.js:21624:14)
at beginWork$1 (react-dom.development.js:27426:14)
at performUnitOfWork (react-dom.development.js:26560:12)
at workLoopConcurrent (react-dom.development.js:26543:5)
at renderRootConcurrent (react-dom.development.js:26505:7)
at performConcurrentWorkOnRoot (react-dom.development.js:25738:38)
at workLoop (scheduler.development.js:266:34)
at flushWork (scheduler.development.js:239:14)
And this is my render:
<Suspense
fallback={<p>Loading portfolio...</p>}
>
<Await
resolve={deferredPortfolio}
errorElement={
<PortfolioError />
}
>
<PortfolioContent />
</Await>
</Suspense>
The promise from the loader takes a few seconds (no more than 10), so I'm not sure what's happening here :thinking:
Any help is really appreciated!
Okay guys, I made a big mistake. Maybe I missed this in the docs.
Check the server.entry.tsx. Update the ABORT_DELAY to be longer.
If you still see it, the client can disconnect and cause it. Otherwise the thing you’re waiting on is past the abort delay
@zackify thanks for your support, let me check! It would be awesome if we can change that delay from a specific route :thinking:
I updated that delay to 20 seconds but I'm getting this other error now:
Error: The render was aborted by the server without a reason.
at abortTask (/frontend/node_modules/react-dom/cjs/react-dom-server.node.development.js:6436:43)
at /frontend/node_modules/react-dom/cjs/react-dom-server.node.development.js:7002:14
at Set.forEach (<anonymous>)
at abort (/frontend/node_modules/react-dom/cjs/react-dom-server.node.development.js:7001:20)
at Timeout.abort [as _onTimeout] (/frontend/node_modules/react-dom/cjs/react-dom-server.node.development.js:7051:7)
at listOnTimeout (node:internal/timers:564:17)
at processTimers (node:internal/timers:507:7)
What do you think? :thinking:
Hi, I'm commenting here since it's the closest related issue to mine. I'm also running into problems with defer(). My problem is that the page is not served until fully rendered.
I had a go at the Discord help channel (tl;dr => A basic <Suspense>/<Await> example won't work on Windows) and we figure it was due WSL. However I can confirm that this is also broken when running remix on directly on Windows (no WSL). Any help appreciated :)
For those interested example below.
// THIS CODE WON'T RUN AS EXPECTED ON WINDOWS REGARDLESS OF WSL
import { defer } from "@remix-run/node";
import { Await, useLoaderData } from "@remix-run/react";
import { Suspense } from "react";
const delay = (ms: number) => new Promise(res => setTimeout(res, ms));
const getData = async () => {
await delay(2000);
return "Hello World";
};
export async function loader() {
return defer({ message: getData() });
}
const DeferredComponent = () => {
const data = useLoaderData<typeof loader>();
return (
<div>
This will always show.
<Suspense fallback={<p>Loading hello world...</p>}>
<Await
resolve={data.message}
errorElement={<p>Error loading message</p>}>
{message => <p>This is your message: {message}</p>}
</Await>
</Suspense>
</div>
);
};
export default DeferredComponent;
Just coming to bump this.
I'm on Windows and have a really weird issue with Suspense that could be the same problems here:
While suspense will work if it's the only thing on the page... If I bring in my full homepage content in Suspense will eventually stop working all together.
None of the components brough in use suspense at all, they are just straight content modules.
I can't for the life of me figure out exactly what's going on or why though
Imagine the embarrasment of figuring it out 30 minutes later:
In my case there was a DomNesting Validation error in the markup, I had a nested Anchor tag.
For some reason this breaks Suspense completely, if you do:
<div>
This will always show.
<Suspense fallback={<p>Loading hello world...</p>}>
<Await
resolve={data.message}
errorElement={<p>Error loading message</p>}>
{message => <p>This is your message: {message}</p>}
</Await>
</Suspense>
<a href="#">
<p>Some text</p>
<a href="#">another bad nested link</a>
</a>
</div>
I'm not sure where this sort of behaviour ends, but a validation error doesn't break React, it still renders normally... so any number of warnings could cause suspense to break.
In a clean repo with just @ebz-jdev 's example it works fine, once you add a nested anchor it breaks
This was fixed in 1.19.0. If issues persist please open a new ticket.