bluwy / create-vite-extra

Extra Vite templates
Other
398 stars 62 forks source link

How to render React Server Component? #58

Closed TheAlexGo closed 1 month ago

TheAlexGo commented 1 month ago

Hello!

I created a project using template-ssr-react-streaming-ts with swc. I created an async Posts component (adding async to the function). When hydrating via entry-client.tsx I get the error "async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding 'use client' to a module that was originally written for the server.".

image

Does anyone know how to fix this?

The error occurs precisely when RSC (Posts.tsx) is hydrated. If only the Card component is hydrated, there will be no error.

My source:

// entry-server.tsx
import { StrictMode } from 'react'
import { type RenderToPipeableStreamOptions, renderToPipeableStream } from 'react-dom/server'
import App from './App'

export function render(_url: string, _ssrManifest?: string, options?: RenderToPipeableStreamOptions) {
  return renderToPipeableStream(
    <StrictMode>
      <App />
    </StrictMode>,
    options
  )
}
// entry-client.tsx
import { StrictMode } from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

ReactDOM.hydrateRoot(
  document.getElementById('root')!,
  <StrictMode>
    <App />
  </StrictMode>
)
// App.tsx
import { Suspense } from 'react';
import Card from './Card';
import { Posts } from './Posts';

export default function App() {
    return (
        <>
            <Suspense fallback={<p>Loading...</p>}>
                <Card />
            </Suspense>
            <Suspense fallback={<p>Loading...</p>}>
                <Posts/>
            </Suspense>
        </>
    )
}
// Card.tsx
// The Card component is the same as in the template.
// Posts.tsx
import { FC, JSX } from 'react';

interface IPost {
    id: number;
    title: string;
}

interface IPosts {

}

const wait = (timeout: number) => {
    return new Promise((resolve) => {
        setTimeout(resolve, timeout);
    })
}

export const Posts: FC<IPosts> = async (): Promise<JSX.Element> => {
    const data: IPost[] = await fetch('https://jsonplaceholder.typicode.com/todos')
        .then(response => response.json());
    await wait(1000);
    return (
        <div>
            {data.map(({ id, title }) => (
                <article key={id}>
                    {title}
                </article>
            ))}
        </div>
    );
};
// server.js
// is the same as in the template
bluwy commented 1 month ago

The templates currently don't support RSC since it requires a much complex bundler setup to get working. A proper running version will also need the next Vite major (v6) environment API.

For now, you can look into some examples like from https://github.com/hi-ogawa/vite-plugins to set it up

bluwy commented 1 month ago

Closing this for now