まだ --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", }, }); }, );