at backyard

Color my life with the chaos of trouble.

Denoでnpmを用いてReact18を動かしてみたサンプル

まだ --unstable をつけないといけないが、 以下のようにして npm モジュールを参照できるようになっているので、この方法を用いてDenoからReactを触ってみたメモ

import * as React from "npm:react";

Denoのバージョン

deno 1.26.1 (release, aarch64-apple-darwin)
v8 10.7.193.3
typescript 4.8.3

Reactのバージョン

タイトルにも書いてあるとおり、18を利用。

DenoでReactを用いた小さなサンプル

まずはReactを動かすだけのサンプル。

ちなみにDenoや、Cloudflare Workersのようなエッジランタイム上では renderToReadableStream を用いるようだ。

https://ja.reactjs.org/docs/react-dom-server.html#rendertoreadablestream

touch main.tsx

main.tsx というファイルを作成し、そちらに以下を書き込む。

import * as React from "npm:react";
import { renderToReadableStream } from "npm:react-dom/server";

const App: React.FC = () => {
  return (
    <html>
      <head>
        <title>React 18 with deno sample</title>
      </head>
      <body>
        <h1>hello world</h1>
      </body>
    </html>
  );
};

Deno.serve(
  { port: 3000 },
  async () => {
    const stream = await renderToReadableStream(<App />);
    return new Response(stream, {
      headers: {
        "Content-Type": "text/html",
        "x-content-type-options": "nosniff"
      },
    });
  },
);

下記のコマンドで実行して、http://localhost:3000 にアクセスすると画面が表示される。

deno run --unstable -A main.tsx

tsxでないとエラーになる。

ちなみに最初 main.ts でファイルを作成してしまっていて、実行時にパースエラーになってしまっていた。
.tsx じゃないと動かない、というのをこちらにも一応書き残しておく

※こんな感じのエラーが出る

error: The module's source code could not be parsed:

ReactのSuspenseを使う

React 18で新たに入った Suspense を使う。

https://ja.reactjs.org/docs/react-api.html#reactsuspense

import * as React from "npm:react";
import { renderToReadableStream } from "npm:react-dom/server";

const WAIT_TIME = 3000;
let done = false;

const App: React.FC = () => {
  return (
    <html>
      <head>
        <title>React 18 with deno sample</title>
      </head>
      <body>
        <React.Suspense fallback={<p>loading...</p>}>
          <Content />
        </React.Suspense>
      </body>
    </html>
  );
};

const Content: React.FC = () => {
  if (done) {
    done = false;

    return <h1>hello world</h1>;
  }

  // 今回の場合、3秒待機した後に"hello world"が表示されるという処理になる
  throw new Promise((resolve) => {
    return setTimeout(() => {
      done = true;
      resolve(true);
    }, WAIT_TIME);
  });
};

Deno.serve(
  { port: 3000 },
  async () => {
    const stream = await renderToReadableStream(<App />);
    return new Response(stream, {
      headers: {
        "Content-Type": "text/html",
        "x-content-type-options": "nosniff",
      },
    });
  },
);