Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render loop when revalidating a page that has Suspense/Await Elements on it #9046

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions contributors.yml
Expand Up @@ -664,3 +664,4 @@
- zainfathoni
- zayenz
- zhe
- lauhon
63 changes: 55 additions & 8 deletions integration/bug-report-test.ts
@@ -1,12 +1,12 @@
import { test, expect } from "@playwright/test";
import { expect, test } from "@playwright/test";

import { PlaywrightFixture } from "./helpers/playwright-fixture.js";
import type { Fixture, AppFixture } from "./helpers/create-fixture.js";
import type { AppFixture, Fixture } from "./helpers/create-fixture.js";
import {
createAppFixture,
createFixture,
js,
} from "./helpers/create-fixture.js";
import { PlaywrightFixture } from "./helpers/playwright-fixture.js";

let fixture: Fixture;
let appFixture: AppFixture;
Expand Down Expand Up @@ -71,11 +71,50 @@ test.beforeAll(async () => {
return (
<div>
{data}
<Link to="/burgers">Other Route</Link>
<Link to="/burgers">Burgers</Link>
<Link to="/spaghetti">spaghetti</Link>
</div>
)
}
`,
"app/routes/spaghetti.tsx": js`
import { Await, defer, useLoaderData, useRevalidator } from "@remix-run/react";
import { Suspense, useEffect, useRef } from "react";

export const loader = () => {
const meatball = Promise.resolve(1);

return defer({ meatball });
};

export default function Spaghetti() {
const { meatball } = useLoaderData();
const { revalidate } = useRevalidator();
const renderCounter = useRef(0);

useEffect(() => {
revalidate();
}, []);

renderCounter.current++;
console.log("render", renderCounter.current);

return (
<>
<p data-testid="render-count">[{renderCounter.current}]</p>
<Suspense>
<Await resolve={meatball.then((amount) => amount * 2)}>
{(val) => (
<div>
<p>Async val: {val}</p>
</div>
)}
</Await>
</Suspense>
</>
);
}
`,

"app/routes/burgers.tsx": js`
export default function Index() {
Expand All @@ -98,20 +137,28 @@ test.afterAll(() => {
// add a good description for what you expect Remix to do 👇🏽
////////////////////////////////////////////////////////////////////////////////

test("[description of what you expect it to do]", async ({ page }) => {
test("Revalidate & Suspense/Await should not cause infinite renders", async ({
page,
}) => {
let app = new PlaywrightFixture(appFixture, page);
// You can test any request your app might get using `fixture`.
let response = await fixture.requestDocument("/");
expect(await response.text()).toMatch("pizza");

// If you need to test interactivity use the `app`
await app.goto("/");
await app.clickLink("/burgers");
await page.waitForSelector("text=cheeseburger");
await app.clickLink("/spaghetti");

await page.waitForSelector("p");

// await page.waitForTimeout(1000);

await expect(page.getByTestId("render-count")).toHaveText("[2]");

await app.poke(20, "/spaghetti");

// If you're not sure what's going on, you can "poke" the app, it'll
// automatically open up in your browser for 20 seconds, so be quick!
// await app.poke(20);

// Go check out the other tests to see what else you can do.
});
Expand Down