diff --git a/packages/react-refresh-utils/internal/helpers.ts b/packages/react-refresh-utils/internal/helpers.ts index de8728308a53bd..4c36c0d2e00a36 100644 --- a/packages/react-refresh-utils/internal/helpers.ts +++ b/packages/react-refresh-utils/internal/helpers.ts @@ -44,7 +44,13 @@ declare const module: { } function isSafeExport(key: string): boolean { - return key === '__esModule' || key === '__N_SSG' || key === '__N_SSP' + return ( + key === '__esModule' || + key === '__N_SSG' || + key === '__N_SSP' || + // TODO: remove this key from page config instead of allow listing it + key === 'config' + ) } function registerExportsForReactRefresh( diff --git a/test/acceptance/ReactRefreshRegression.test.js b/test/acceptance/ReactRefreshRegression.test.js index fd7d98d1e9ef4e..3f33cebd989ffa 100644 --- a/test/acceptance/ReactRefreshRegression.test.js +++ b/test/acceptance/ReactRefreshRegression.test.js @@ -67,3 +67,190 @@ test('styled-components hydration mismatch', async () => { await cleanup() }) + +// https://github.com/vercel/next.js/issues/13978 +test('can fast refresh a page with getStaticProps', async () => { + const [session, cleanup] = await sandbox() + + await session.patch( + 'pages/index.js', + ` + import { useCallback, useState } from 'react' + + export function getStaticProps() { + return { props: { } } + } + + export default function Index() { + const [count, setCount] = useState(0) + const increment = useCallback(() => setCount(c => c + 1), [setCount]) + return ( +
+

{count}

+ +
+ ) + } + ` + ) + + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('0') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') + + await session.patch( + 'pages/index.js', + ` + import { useCallback, useState } from 'react' + + export default function Index() { + const [count, setCount] = useState(0) + const increment = useCallback(() => setCount(c => c + 1), [setCount]) + return ( +
+

Count: {count}

+ +
+ ) + } + ` + ) + + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 2') + + await cleanup() +}) + +// https://github.com/vercel/next.js/issues/13978 +test('can fast refresh a page with getServerSideProps', async () => { + const [session, cleanup] = await sandbox() + + await session.patch( + 'pages/index.js', + ` + import { useCallback, useState } from 'react' + + export function getServerSideProps() { + return { props: { } } + } + + export default function Index() { + const [count, setCount] = useState(0) + const increment = useCallback(() => setCount(c => c + 1), [setCount]) + return ( +
+

{count}

+ +
+ ) + } + ` + ) + + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('0') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') + + await session.patch( + 'pages/index.js', + ` + import { useCallback, useState } from 'react' + + export default function Index() { + const [count, setCount] = useState(0) + const increment = useCallback(() => setCount(c => c + 1), [setCount]) + return ( +
+

Count: {count}

+ +
+ ) + } + ` + ) + + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 2') + + await cleanup() +}) + +// https://github.com/vercel/next.js/issues/13978 +test('can fast refresh a page with config', async () => { + const [session, cleanup] = await sandbox() + + await session.patch( + 'pages/index.js', + ` + import { useCallback, useState } from 'react' + + export const config = {} + + export default function Index() { + const [count, setCount] = useState(0) + const increment = useCallback(() => setCount(c => c + 1), [setCount]) + return ( +
+

{count}

+ +
+ ) + } + ` + ) + + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('0') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') + + await session.patch( + 'pages/index.js', + ` + import { useCallback, useState } from 'react' + + export default function Index() { + const [count, setCount] = useState(0) + const increment = useCallback(() => setCount(c => c + 1), [setCount]) + return ( +
+

Count: {count}

+ +
+ ) + } + ` + ) + + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 2') + + await cleanup() +})