Reactに関する書籍・web記事を見ていると、場合よってはjsx/tsxファイルの1行目にimport React from 'react'
を書かなければならないという記述があります。
一方で、"この記述は古いバージョンの時の話で最近はそうではない"という記載もあります。
今回、ここの仕組みを理解するために確認してみました。
JSXのタグ
reactでは、<App />
などjsxタグを記述することができますが、これはJavaScriptとしてvalidな形式ではありません。
ReactではjsxタグをJavaScriptの関数に変換する処理を挟む必要があります。
JSXタグの変換
jsxタグからJavaScriptの関数への変換ですが、React17.0以降では変換後としてJavaScriptに変換できる形式が増えています。
17.0ではReact.createElement
の形式でしたが、17.0からはこれに加えてjsx
の形式に変換することもできます。
React17.0より前
- 変換前 (=あなたが書くReactのコード)
import React from 'react';
function App() {
return <h1>Hello World</h1>;
}
- 変換後 (=Reactの仕組みで変換された後のコード)
- 変換後のコードに
React
が含まれていることに注意
- 変換後のコードに
import React from 'react';
function App() {
return React.createElement('h1', null, 'Hello world');
}
React17.0以降
- 変換前
function App() {
return <h1>Hello World</h1>;
}
- 変換後
- 変換後のコードへimportを自動で追加してくれていることに注意
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
Webpackの定義
Webpackの定義ファイルであり`webpack.config.jsでは、以下のような形で*.tsxファイルに対する変換を
ts-loader`に行わせることを指定できます。
module.exports = {
mode: 'development',
devtool: 'inline-source-map',
entry: './src/index.ts',
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
loader: 'ts-loader',
}
]
}
}
TypeScript
loaderにts-loader
を使った時、JSXタグをjsの関数へ変換する処理はTypeScriptが行います。
ということは、TypeScriptが(React.createElementではなく)、jsxに変換できるよう対応している必要がありますが、これはTypeScriptのバージョン4.1以降でサポートされています。
JSXのタグをReact.createElementとjsxのどちらの形式にするかは、tsconfig.jsonのcompilerOptions
の中にあるjsx
で定義することができます。
(このドキュメントではこの設定項目を、これ以降compilerOptions.jsx
と記載します)
定義の書き方
tsconfig.jsonのcompilerOptions.jsx
ですが、今回確認したい範囲でいうと以下の形式があります。
- reactを指定する
{
"compilerOptions": {
"jsx": "react",
- react-jsxを指定する
{
"compilerOptions": {
"jsx": "react-jsx",
- 定義自体を書かない(コメントアウトなど)
{
"compilerOptions": {
// "jsx": "",
compilerOptions.jsx
の意味
compilerOptions.jsx
の意味は、TypeScript: TSConfig Referenceに説明があります。
Controls how JSX constructs are emitted in JavaScript files. This only affects output of JS files that started in .tsx files.
react: Emit .js files with JSX changed to the equivalent React.createElement calls
react-jsx: Emit .js files with the JSX changed to _jsx calls
react-jsxdev: Emit .js files with the JSX changed to _jsx calls
preserve: Emit .jsx files with the JSX unchanged
react-native: Emit .js files with the JSX unchanged
書いてあるとおり、パラメータ値にraect
を指定すると、React.createElement
の形式に変換されます。
react-jsx
だと、_jsx
の形式に変換されます。
react-jsxdev
も、_jsx
の形式に変換されてますが、こちらは変換後のコードに元ファイルの番号などのデバッグ情報が追加されます。
import React from 'react';
を書かなかった時の振る舞い
コードの頭にimport React from 'react';を書かずに、
compilerOptions.jsx`に確認した値をセットしてコンパイルした時の出力を確認します。
- reactを指定する
jsxのパラメータに"react"を指定した場合、TS17004エラーが出ます。
$ npm run dev
ERROR in App.tsx
./src/App.tsx 45:16-40
[tsl] ERROR in App.tsx(45,17)
TS17004: Cannot use JSX unless the '--jsx' flag is provided.
- react-jsxを指定する
この定義は正しいので、エラーにならなず正しくビルドできます。
- 定義自体を書かない(コメントアウトなど)
jsxのパラメータを指定しない場合、TS2686エラーが出ます。
$ npm run dev
ERROR in App.tsx
./src/App.tsx 45:16-40
[tsl] ERROR in App.tsx(45,17)
TS2686: 'React' refers to a UMD global, but the current file is a module. Consider adding an import instead.