diff --git a/.eslintrc b/.eslintrc index 859a94089..9b7e2be19 100644 --- a/.eslintrc +++ b/.eslintrc @@ -31,12 +31,20 @@ "node": true, "jest": true }, + "overrides": [ + { + "files": ["e2e/**/*.ts"], + "rules": { + "testing-library/prefer-screen-queries": "off" + } + } + ], "rules": { "func-names": [2, "as-needed"], "no-shadow": 0, "@typescript-eslint/no-shadow": 2, "@typescript-eslint/explicit-function-return-type": 0, - "@typescript-eslint/no-unused-vars": [0, {"argsIgnorePattern": "^_"}], + "@typescript-eslint/no-unused-vars": [0, { "argsIgnorePattern": "^_" }], "@typescript-eslint/no-use-before-define": 0, "@typescript-eslint/ban-ts-ignore": 0, "@typescript-eslint/no-empty-function": 0, @@ -44,7 +52,10 @@ "@typescript-eslint/no-var-requires": 0, "@typescript-eslint/no-explicit-any": 0, "@typescript-eslint/explicit-module-boundary-types": 0, - "@typescript-eslint/consistent-type-imports": [2, {"prefer": "type-imports"}], + "@typescript-eslint/consistent-type-imports": [ + 2, + { "prefer": "type-imports" } + ], "@typescript-eslint/ban-types": 0, "react-hooks/rules-of-hooks": 2, "react-hooks/exhaustive-deps": 1, diff --git a/e2e/site/app/issue-2702/page.tsx b/e2e/site/app/issue-2702/page.tsx new file mode 100644 index 000000000..3518c8721 --- /dev/null +++ b/e2e/site/app/issue-2702/page.tsx @@ -0,0 +1,9 @@ +import Comp from './reproduction' + +export default function Page() { + return ( +
+ +
+ ) +} diff --git a/e2e/site/app/issue-2702/reproduction.tsx b/e2e/site/app/issue-2702/reproduction.tsx new file mode 100644 index 000000000..0544f3334 --- /dev/null +++ b/e2e/site/app/issue-2702/reproduction.tsx @@ -0,0 +1,41 @@ +'use client' +import useSWR, { preload } from 'swr' +import { Suspense, use, useEffect, useState } from 'react' + +const sleep = (time: number, data: string) => + new Promise(resolve => { + setTimeout(() => resolve(data), time) + }) + +const Bug = () => { + const a = use(preload('a', () => sleep(1000, 'a'))) + const { data: b } = useSWR('b', () => sleep(2000, 'b'), { + suspense: true + }) + useState(b) + return ( +
+ {a},{b} +
+ ) +} + +const Comp = () => { + const [loading, setLoading] = useState(true) + + // To prevent SSR + useEffect(() => { + setLoading(false) + }, []) + + if (loading) { + return Loading... + } + return ( + fetching}> + + + ) +} + +export default Comp diff --git a/e2e/test/issue-2702-too-many-hooks.ts b/e2e/test/issue-2702-too-many-hooks.ts new file mode 100644 index 000000000..d4efeffe2 --- /dev/null +++ b/e2e/test/issue-2702-too-many-hooks.ts @@ -0,0 +1,14 @@ +import { test, expect } from '@playwright/test' + +test.describe('issue 2702', () => { + test('should not crash with too many hooks', async ({ page }) => { + // Navigate to the test page + await page.goto('./issue-2702', { waitUntil: 'networkidle' }) + + // Wait for the page to be fully loaded and interactive + await expect(page.getByText('fetching')).toBeVisible() + + // Verify that the component renders correctly + await expect(page.getByText('a,b')).toBeVisible() + }) +})