diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx
index b9ebabfd056ae..88258105aa786 100644
--- a/packages/next/server/render.tsx
+++ b/packages/next/server/render.tsx
@@ -571,7 +571,11 @@ export async function renderToHTML(
}
// url will always be set
- const routerIsReady = !!(getServerSideProps || hasPageGetInitialProps)
+ const routerIsReady = !!(
+ getServerSideProps ||
+ hasPageGetInitialProps ||
+ (!defaultAppGetInitialProps && !isSSG)
+ )
const router = new ServerRouter(
pathname,
query,
diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts
index 8f8bc803e2eb7..ff7692be29198 100644
--- a/packages/next/shared/lib/router/router.ts
+++ b/packages/next/shared/lib/router/router.ts
@@ -627,6 +627,7 @@ export default class Router implements BaseRouter {
this.isReady = !!(
self.__NEXT_DATA__.gssp ||
self.__NEXT_DATA__.gip ||
+ (self.__NEXT_DATA__.appGip && !self.__NEXT_DATA__.gsp) ||
(!autoExportDynamic &&
!self.location.search &&
!process.env.__NEXT_HAS_REWRITES)
diff --git a/test/integration/router-is-ready-app-gip/pages/_app.js b/test/integration/router-is-ready-app-gip/pages/_app.js
new file mode 100644
index 0000000000000..c49ee290cf782
--- /dev/null
+++ b/test/integration/router-is-ready-app-gip/pages/_app.js
@@ -0,0 +1,15 @@
+export default function TestApp({ Component, pageProps }) {
+ return
appGip page
+{JSON.stringify(props)}
+ > + ) +} diff --git a/test/integration/router-is-ready-app-gip/pages/gsp.js b/test/integration/router-is-ready-app-gip/pages/gsp.js new file mode 100644 index 0000000000000..f9c637c9fecd6 --- /dev/null +++ b/test/integration/router-is-ready-app-gip/pages/gsp.js @@ -0,0 +1,32 @@ +import { useRouter } from 'next/router' +import { useLayoutEffect } from 'react' + +export default function Page(props) { + const router = useRouter() + + if (typeof window !== 'undefined') { + // eslint-disable-next-line react-hooks/rules-of-hooks + useLayoutEffect(() => { + if (!window.isReadyValues) { + window.isReadyValues = [] + } + window.isReadyValues.push(router.isReady) + }, [router]) + } + + return ( + <> +gsp page
+{JSON.stringify(props)}
+ > + ) +} + +export const getStaticProps = () => { + return { + props: { + hello: 'world', + random: Math.random(), + }, + } +} diff --git a/test/integration/router-is-ready-app-gip/pages/invalid.js b/test/integration/router-is-ready-app-gip/pages/invalid.js new file mode 100644 index 0000000000000..d5c2d696cdd2c --- /dev/null +++ b/test/integration/router-is-ready-app-gip/pages/invalid.js @@ -0,0 +1,15 @@ +import { useRouter } from 'next/router' + +export default function Page(props) { + // eslint-disable-next-line + const router = useRouter() + + // console.log(router.isReady) + + return ( + <> +invalid page
+{JSON.stringify(props)}
+ > + ) +} diff --git a/test/integration/router-is-ready-app-gip/test/index.test.js b/test/integration/router-is-ready-app-gip/test/index.test.js new file mode 100644 index 0000000000000..ebcb4e689e0d4 --- /dev/null +++ b/test/integration/router-is-ready-app-gip/test/index.test.js @@ -0,0 +1,68 @@ +/* eslint-env jest */ + +import { join } from 'path' +import webdriver from 'next-webdriver' +import { + findPort, + launchApp, + killApp, + nextStart, + nextBuild, + File, +} from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 1) + +let app +let appPort +const appDir = join(__dirname, '../') +const invalidPage = new File(join(appDir, 'pages/invalid.js')) + +function runTests(isDev) { + it('isReady should be true immediately for pages without getStaticProps', async () => { + const browser = await webdriver(appPort, '/appGip') + expect(await browser.eval('window.isReadyValues')).toEqual([true]) + }) + + it('isReady should be true immediately for pages without getStaticProps, with query', async () => { + const browser = await webdriver(appPort, '/appGip?hello=world') + expect(await browser.eval('window.isReadyValues')).toEqual([true]) + }) + + it('isReady should be true immediately for getStaticProps page without query', async () => { + const browser = await webdriver(appPort, '/gsp') + expect(await browser.eval('window.isReadyValues')).toEqual([true]) + }) + + it('isReady should be true after query update for getStaticProps page with query', async () => { + const browser = await webdriver(appPort, '/gsp?hello=world') + expect(await browser.eval('window.isReadyValues')).toEqual([false, true]) + }) +} + +describe('router.isReady with appGip', () => { + describe('dev mode', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + }) + afterAll(async () => { + await killApp(app) + invalidPage.restore() + }) + + runTests(true) + }) + + describe('production mode', () => { + beforeAll(async () => { + await nextBuild(appDir) + + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + + runTests() + }) +})