1

Is there a way to keep the component suspended on the server side and render it on the client with React 18? I tried using this pattern https://beta.reactjs.org/reference/react/Suspense#providing-a-fallback-for-server-errors-and-server-only-content. It works as expected but the problem is that if I use this pattern in multiple components the console gets filled with errors that were thrown on the server side by the component so it would remain suspended. Also later all these errors get passed to the client side

<Suspense fallback={<Loading />}>
  <Chat />
</Suspense>

function Chat() {
  if (typeof window === 'undefined') {
    throw Error('Chat should only render on the client.');
  }
  // ...
}

Server-side error:

Error: Chat should only render on the client.",
        "at Chat (/Users/Chat.tsx:86:19)

Client-side error:

Uncaught Error: Chat should only render on the client.
    at updateDehydratedSuspenseComponent (react-dom.development.js:20662:1)
    at updateSuspenseComponent (react-dom.development.js:20362:1)
    at beginWork (react-dom.development.js:21624:1)
    at beginWork$1 (react-dom.development.js:27426:1)
    at performUnitOfWork (react-dom.development.js:26557:1)
    at workLoopSync (react-dom.development.js:26466:1)
    at renderRootSync (react-dom.development.js:26434:1)
    at performConcurrentWorkOnRoot (react-dom.development.js:25738:1)
    at workLoop (scheduler.development.js:266:1)
    at flushWork (scheduler.development.js:239:1)

Is there a way to achieve the same functionality but prevent errors to appear in the console without third-party SSR libraries like Next.js? My project uses the express and react-dom/server to do the SSR.

zimex
  • 713
  • 1
  • 5
  • 10

1 Answers1

0

If you're using Next.js for SSR you can mark the component for CSR with 'use client'; at the top of the file, see these Server and Client Components docs.

Suspense is simply a fallback for asynchronous rendering but will still render on the server, as far as I know.

Workaround with useEffect:

function Wrapper() {
  const [client, setClient] = useState(false)

  useEffect(() => {
    setClient(true)
  }, [])

  if (!client) return null

  return <Chat />
}
matthiasgiger
  • 1,086
  • 9
  • 17
  • My project uses the express and react-dom/server to do the SSR. How this could be achieved in this environment? – zimex Feb 01 '23 at 21:24
  • I think there the idea is that Client components end in `Chat.client.tsx`. The ending will let the bundler know it's a client component. However, I'm not sure your setup will support this already. – matthiasgiger Feb 01 '23 at 21:34
  • As a workaround you could also make use of the fact that useEffect will not run on the server and set some state there in a wrapping component that will only cause your component to be rendered once useEffect is run. – matthiasgiger Feb 01 '23 at 21:37
  • I'm so confused. Why did they put a throwing error pattern in the doc if it has this side effect? I'm currently using your workaround but I was curious about how to execute the pattern provided in the doc. – zimex Feb 01 '23 at 21:45
  • I see, they have another pattern there to ignore errors inside Suspense and try again on the Client (not displaying the SSR errors again). You are rightly confused as according to the documentation no errors should appear. In theory this should also work with multiple components, but I guess these docs as well as React Server Components are still in Beta state. – matthiasgiger Feb 01 '23 at 21:59
  • Make sure you have [Streaming](https://beta.reactjs.org/reference/react-dom/server#server-apis-for-nodejs-streams) enabled when rendering as this isn't supposed to work with regular rendering. – matthiasgiger Feb 01 '23 at 22:02