From 6279dba3b8fe1d6b881145d6079db6ffb26e593d Mon Sep 17 00:00:00 2001 From: Shu Ding Date: Mon, 19 Sep 2022 20:08:52 +0200 Subject: [PATCH] Avoid direct React client API imports in the server graph (#40686) We have heuristic checks in the SWC transform to make sure you are not using client-only APIs such as `useState` inside the Server Components graph. However inside our server graph compilation we also have to import the framework and renderer itself (not just the component), and some utility files import these client APIs (because they can be shared by the SSR or client code). Hence we have errors like https://github.com/vercel/next.js/actions/runs/3083270196/jobs/4984135491. To manually opt-out these errors, you can do `import React from 'react'` and use these APIs via `React.useState`. cc @feedthejim ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have a helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have a helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The "examples guidelines" are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md) --- packages/next/pages/_document.tsx | 16 ++++++++++++---- packages/next/shared/lib/flush-effects.tsx | 10 ++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 9d601b89bd557..26564e04cc50b 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -1,4 +1,4 @@ -import React, { Component, ReactElement, ReactNode, useContext } from 'react' +import React, { ReactElement, ReactNode, useContext } from 'react' import { OPTIMIZED_FONT_PROVIDERS, NEXT_BUILTIN_DOCUMENT, @@ -353,7 +353,13 @@ function getAmpPath(ampPath: string, asPath: string): string { return ampPath || `${asPath}${asPath.includes('?') ? '&' : '?'}amp=1` } -export class Head extends Component { +// Use `React.Component` to avoid errors from the RSC checks because +// it can't be imported directly in Server Components: +// +// import { Component } from 'react' +// +// More info: https://github.com/vercel/next.js/pull/40686 +export class Head extends React.Component { static contextType = HtmlContext context!: React.ContextType @@ -899,7 +905,7 @@ function handleDocumentScriptLoaderItems( __NEXT_DATA__.scriptLoader = scriptLoaderItems } -export class NextScript extends Component { +export class NextScript extends React.Component { static contextType = HtmlContext context!: React.ContextType @@ -1104,7 +1110,9 @@ export function Main() { * `Document` component handles the initial `document` markup and renders only on the server side. * Commonly used for implementing server side rendering for `css-in-js` libraries. */ -export default class Document

extends Component { +export default class Document

extends React.Component< + DocumentProps & P +> { /** * `getInitialProps` hook returns the context object with the addition of `renderPage`. * `renderPage` callback executes `React` rendering logic synchronously to support server-rendering wrappers diff --git a/packages/next/shared/lib/flush-effects.tsx b/packages/next/shared/lib/flush-effects.tsx index 4aae4f1d73ff4..7d9a0c1ae42d0 100644 --- a/packages/next/shared/lib/flush-effects.tsx +++ b/packages/next/shared/lib/flush-effects.tsx @@ -1,8 +1,14 @@ -import React, { createContext, useContext } from 'react' +import React, { useContext } from 'react' export type FlushEffectsHook = (callbacks: () => React.ReactNode) => void -export const FlushEffectsContext = createContext( +// Use `React.createContext` to avoid errors from the RSC checks because +// it can't be imported directly in Server Components: +// +// import { createContext } from 'react' +// +// More info: https://github.com/vercel/next.js/pull/40686 +export const FlushEffectsContext = React.createContext( null as any )