From bc90ef3af1a398c9891a5ff5e844d188bfddbe31 Mon Sep 17 00:00:00 2001 From: phuctm97 <25026967+phuctm97@users.noreply.github.com> Date: Fri, 3 Feb 2023 17:01:42 +0700 Subject: [PATCH] Reproduce issue https://github.com/vercel/next.js/issues/44083 --- components/example/component.tsx | 31 +++++++++++++++++++++++++ components/example/fallback.tsx | 9 ++++++++ components/example/index.tsx | 22 ++++++++++++++++++ components/provider/component.tsx | 16 +++++++++++++ components/provider/context.ts | 4 ++++ components/provider/index.tsx | 15 ++++++++++++ package.json | 2 ++ pages/_app.tsx | 13 ++++++++--- pages/api/hello.ts | 13 ----------- pages/index.tsx | 38 ++++++++++--------------------- yarn.lock | 17 ++++++++++++++ 11 files changed, 138 insertions(+), 42 deletions(-) create mode 100644 components/example/component.tsx create mode 100644 components/example/fallback.tsx create mode 100644 components/example/index.tsx create mode 100644 components/provider/component.tsx create mode 100644 components/provider/context.ts create mode 100644 components/provider/index.tsx delete mode 100644 pages/api/hello.ts diff --git a/components/example/component.tsx b/components/example/component.tsx new file mode 100644 index 0000000..2ae86a4 --- /dev/null +++ b/components/example/component.tsx @@ -0,0 +1,31 @@ +import styles from "@/styles/Home.module.css"; + +import Image from "next/image"; +import { atom, useRecoilValue } from "recoil"; + +const componentAtom = atom({ + key: "component", + default: new Promise((resolve) => setTimeout(resolve, 5000)), // Example data fetching or async operation +}); + +export default function Component() { + // This will suspend the component + // Recoil is being used just to demonstrate the issue happen when this component being suspended + // If you use React Developer Tools to suspend this component manually, the issue will still happen + useRecoilValue(componentAtom); + return ( +
+ Next.js Logo +
+ 13 +
+
+ ); +} diff --git a/components/example/fallback.tsx b/components/example/fallback.tsx new file mode 100644 index 0000000..639becf --- /dev/null +++ b/components/example/fallback.tsx @@ -0,0 +1,9 @@ +import styles from "@/styles/Home.module.css"; + +export function Fallback() { + return ( +
+

Loading…

+
+ ); +} diff --git a/components/example/index.tsx b/components/example/index.tsx new file mode 100644 index 0000000..0a6ed38 --- /dev/null +++ b/components/example/index.tsx @@ -0,0 +1,22 @@ +import dynamic from "next/dynamic"; +import { Suspense, useContext } from "react"; + +import { Context } from "../provider"; +import { Fallback } from "./fallback"; + +const Component = dynamic(() => import("./component")); + +// Example component that depends on Context (see ../provider/context.ts) +export function Example() { + const context = useContext(Context); + if (!context) return ; // Render fallback when context is not available (dynamic import/async data fetching is still in progress) + // [ISSUE] Everything works fine until this line + // (see ./component.tsx) will suspend for 5 seconds (e.g. fetch data, etc.) + // Within those 5 seconds, SHOULD be rendered, but IT IS NOT, nothing is rendered + // Only after 5 seconds, is render + return ( + }> + + + ); +} diff --git a/components/provider/component.tsx b/components/provider/component.tsx new file mode 100644 index 0000000..9761d64 --- /dev/null +++ b/components/provider/component.tsx @@ -0,0 +1,16 @@ +import { PropsWithChildren, useEffect, useState } from "react"; +import { RecoilRoot } from "recoil"; + +import { Context } from "./context"; + +export default function Component({ children }: PropsWithChildren) { + // Provide example context after being mounted (client-side only). In real-world, some dynamic/asynchronous data fetching would be done here; + const [context, setContext] = useState(); + useEffect(() => setContext({}), [setContext]); + return ( + + {/* Example heavy dependencies */} + {children} + + ); +} diff --git a/components/provider/context.ts b/components/provider/context.ts new file mode 100644 index 0000000..4b85885 --- /dev/null +++ b/components/provider/context.ts @@ -0,0 +1,4 @@ +import { createContext } from "react"; + +// Example context whose value will be provided dynamically/asynchronously in by Provider component (see ./index.tsx) +export const Context = createContext(undefined); \ No newline at end of file diff --git a/components/provider/index.tsx b/components/provider/index.tsx new file mode 100644 index 0000000..c3603b6 --- /dev/null +++ b/components/provider/index.tsx @@ -0,0 +1,15 @@ +import dynamic from "next/dynamic"; +import { Fragment, PropsWithChildren, Suspense } from "react"; + +// Dynamically import the actual Provider component because it contains some heavy dependencies +const Component = dynamic(() => import("./component")); + +export * from "./context"; + +export function Provider(props: PropsWithChildren) { + return ( + }> + + + ); +} \ No newline at end of file diff --git a/package.json b/package.json index 80c6132..75a5923 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,10 @@ "@types/react": "18.0.27", "@types/react-dom": "18.0.10", "next": "13.1.6", + "prettier": "^2.8.3", "react": "18.2.0", "react-dom": "18.2.0", + "recoil": "^0.7.6", "typescript": "4.9.5" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 021681f..ea46d04 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,6 +1,13 @@ -import '@/styles/globals.css' -import type { AppProps } from 'next/app' +import "@/styles/globals.css"; + +import type { AppProps } from "next/app"; + +import { Provider } from "@/components/provider"; export default function App({ Component, pageProps }: AppProps) { - return + return ( + + + + ); } diff --git a/pages/api/hello.ts b/pages/api/hello.ts deleted file mode 100644 index f8bcc7e..0000000 --- a/pages/api/hello.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from 'next' - -type Data = { - name: string -} - -export default function handler( - req: NextApiRequest, - res: NextApiResponse -) { - res.status(200).json({ name: 'John Doe' }) -} diff --git a/pages/index.tsx b/pages/index.tsx index 3a20955..613e07c 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,9 +1,11 @@ -import Head from 'next/head' -import Image from 'next/image' -import { Inter } from '@next/font/google' -import styles from '@/styles/Home.module.css' +import styles from "@/styles/Home.module.css"; -const inter = Inter({ subsets: ['latin'] }) +import Head from "next/head"; +import Image from "next/image"; +import { Inter } from "@next/font/google"; +import { Example } from "@/components/example"; + +const inter = Inter({ subsets: ["latin"] }); export default function Home() { return ( @@ -26,7 +28,7 @@ export default function Home() { target="_blank" rel="noopener noreferrer" > - By{' '} + By{" "} Vercel Logo -
- Next.js Logo -
- 13 -
-
+ {/* [BUG] This example component should render "Loading…" for 5 seconds then render Next.js logo but it doesn't + Instead, it renders nothing for 5 seconds then render Next.js logo. */} +
- ) + ); } diff --git a/yarn.lock b/yarn.lock index c427e89..318ba39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -130,6 +130,11 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== +hamt_plus@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hamt_plus/-/hamt_plus-1.0.2.tgz#e21c252968c7e33b20f6a1b094cd85787a265601" + integrity sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA== + "js-tokens@^3.0.0 || ^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -186,6 +191,11 @@ postcss@8.4.14: picocolors "^1.0.0" source-map-js "^1.0.2" +prettier@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" + integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== + react-dom@18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" @@ -201,6 +211,13 @@ react@18.2.0: dependencies: loose-envify "^1.1.0" +recoil@^0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/recoil/-/recoil-0.7.6.tgz#75297ecd70bbfeeb72e861aa6141a86bb6dfcd5e" + integrity sha512-hsBEw7jFdpBCY/tu2GweiyaqHKxVj6EqF2/SfrglbKvJHhpN57SANWvPW+gE90i3Awi+A5gssOd3u+vWlT+g7g== + dependencies: + hamt_plus "1.0.2" + scheduler@^0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"