https://zenn.dev/uhyo/articles/react17-new-jsx-transform

今日発表された公式ブログの記事によれば、React17では新しいJSXの変換がサポートされます。これはどういうことなのか、我々にどういう影響があるのかをまとめました。

JSXの変換とは

ほとんどの人は、Reactを使う際に以下のようなJSX記法を使っているはずです。具体的には次のようなもので、<div>のようなHTMLに近い記法がJSXです。

const Foo = () => {
  return <div>
    <p id="a">I am foo</p>
    <p key="b">I am foo2</p>>
  </div>;
}

これらは純粋なJavaScriptではないため、そのままでは実行できません。そのため、何らかの方法でただのJavaScriptに変換する必要があります。現代では、それを担うのはBabelやTypeScriptです。これらによって、上記のJSXを含むコードは次のように変換されます(下記はTypeScriptによる変換の場合)。

const Foo = () => {
  return React.createElement("div", null,
    React.createElement("p", { id: "a" }, "I am foo"),
    React.createElement("p", { key: "b" }, "I am foo2"));
};

React 17では、この変換結果が変わります(より正確には、これまでの方式もサポートしつつ新しい方式のサポートが加わります)。

新しいJSXの変換結果

React 17では以下のような変換結果となります(TypeScript 4.1 betaで対応しているはずですが、なぜかうまく動かなかったのでこれは手書きです)。

import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";

const Foo = () => {
  return _jsxs("div", {
    children: [
      _jsx("p", { id: "a", children: "I am foo" }, void 0),
      _jsx("p", { children: "I am foo2" }, "b"),
    ]
  }, void 0);
};

まず最初に注意すべきことは、あくまでJSXからの変換結果が変わっただけであり、我々が慣れ親しんでいるJSXの書き方自体は何も変わっていないということです(keyなどに絡んでエッジケースがあったりしますが)。ですから、この話はどちらかというとReactの内部実装の変化の話であるということです(JSXを変換する必要があるのでBabelやTypeScriptといった周辺のエコシステムが付き合わされていますが)。

ちなみに、この新しい変換自体は2019年前半から構想されていたもので(RFCがあります)、足掛け1年以上かけてようやく実現まで漕ぎ着けたことになりますね。

その上で変化した点を見ていくと、いくつか挙げられます。

jsx, jsxsreact/jsx-runtimeからインポートされる

従来はJSXのタグはReact.createElement("div", ...)のような関数呼び出しに変換されていましたが、これが_jsxs("div", ...)のような関数呼び出しに変換されました。関数名が変わったのはさして重要ではないのですが、このjsxやjsxsのインポートがトランスパイラによって自動的に追加されるという点が重要です。

従来、JSX記法を使用するファイルでは以下の記述を含める必要がありました。

これは、JSXがReact.createElementに変換されるのであらかじめこちら側でReactを用意しておく必要があったからです。新しい記法では、Reactをあらかじめインポートしておく必要がありません。

利用者的にはこれは嬉しい変更ですが、React側の思惑としてはこれは副作用でしょう。というのも、古い記法でもReactを自動的にインポートするのはできないわけでもないという点と、また前述のRFCに以下のような記述があるからです。