deno deploy上でReactなどのライブラリを使ってJSXを記述した際に、「ReferenceError: h is not defined
」というエラーが発生することがあります。これは、JSXを使ってSSRする際のオプションの設定方法が原因です。
特にこのエラーは「本番環境ではエラーが出るがローカル開発ではエラーが出ない」という事態になりやすく、なかなか開発中は気づきにくいです。
この記事では、このエラーの解決方法を紹介します。
原因
まず、DenoはJSXを直接実行することができます。ただし実行の際に、裏側ではJSXが素のJavaScriptに変換されたうえでJavaScriptに渡されます。
const a = <div>hello world</div>
React.createElement(div, null, "hello world"));
上の例ではReact.createElement
に変換されていますが、環境や設定によってどの関数に変換されるのかが変わります。
具体的には、デフォルトでは以下のようにそれぞれ変換されます。
-
Deno 1.x系で動かす場合 →
React.createElement
という名前の関数へ変換 -
deno deployで動かす場合 →
h
という名前の関数へ変換
これらの違いから、ローカル開発でDenoを使用していた時は正常に動作するが、本番環境であるdeno deployに持っていくと動かないという事態が発生します。
なぜこうなっているのか:
- TypeScript本体のデフォルト値は
React.createElement
なので、DenoのデフォルトもReact.createElement
になっている。 - ただしDenoではReactが主流ではないので、デフォルト値がReactという名前の関数になっている意味はあまりない。そのためこのデフォルト値を
h
という名前の関数に変更しようという議論が行われた。これは破壊的変更なので、近々リリースされるDeno 2.0で導入しよう、となった。 - そのタイミングでdeno deployが提供開始されたため、deno deployではデフォルト値が
h
という名前になった - ところが、Deno 2.0リリースが様々な事情で延期されたため、Deno本体のデフォルト値は現在も
React.createElement
のままである
という流れのようです。
解決方法
JSXの変換先を明示的に指定する必要があります。
Reactなどのjsxトランスパイラをインストールしている個所すべてに、JSXプラグマを追記します。
JSXプラグマというのは、/** @jsx <JSXの変換先> */
という形のコメントです。
import React from "./server_deps.ts";
/** @jsxFrag React.Fragment */
/** @jsx React.createElement */
import React from "./server_deps.ts";
このコメントを追記すると、どの関数に変換すればよいのかをトランスパイラに指示することができます。そのため、環境によってトランスパイル結果が変わることが無くなります。
解決方法2
上の解決方法では、JSXを使用するファイルすべてにJSXプラグマを追記する必要がありました。
これが面倒な場合は、あまりメジャーではありませんが、deno.json
というファイルを作ってその中にtscondigのオプションを書く方法もあります。
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment"
}
}
まとめ
- deno deployとDeno 1.0系を併用する場合、JSXがどの形にトランスパイルされるのかを指定する必要がある。
- 指定方法は、JSXプラグマで指定する方法と、
deno.json
で指定する方法の2種類ある。
- 指定方法は、JSXプラグマで指定する方法と、