React v19
〜 UMD でローカル環境 〜
2026-04-16 作成 福島
TOP > tips > reactv19
[ TIPS | TOYS | OTAKU | LINK | MOVIE | CGI | AvTitle | ConfuTerm | HIST | AnSt | Asob | Shell | GBC | LLM ]

0. 前置き

React.js は v19 から公式な UMD が非提供になりました。
esm.sh 等の代替案が提示されていますが、状況は好転していません。
esm.sh は、v18 まで提供されていた UMD の .js を一度に取得する構造になっておらず、再帰的な取得が必要です。

プログラミングの環境には一貫性が必要です。
学習時には不確定要素を排除する必要があり、業務ではライブラリのサニタイズチェックも必要です。
動作に常時インターネットが必須とあっては、これらを保証することができません。
(<script src="https://よそ様のドメイン/いろいろ.js"> の心配が消えない)

本稿では Linux 版の npm を使用して UMD (IIFE)*1 の React.js を作成します。
WSL2LXC でも作成できるので、React.js を作成したら環境ごと削除しても良いでしょう。
*1ESM は import でローカルファイルを読み込めないためスタンドアロンの環境では UMD を使わざるを得ません。


1. React.js の作成

1-1. Node.js をインストールする。
RHEL 系はこちら。

$ su
# dnf install -y nodejs
# exit
$ node -v ; npm -v
v22.22.0
10.9.4

Ubuntu はこちら。

$ sudo apt update
$ sudo apt install -y nodejs npm
$ node -v ; npm -v
v18.19.1
9.2.0
1-2. ビルドに必要なモジュールをインストールする。
$ mkdir -p ./my-app/
$ cd ./my-app/
/my-app/$ npm init -y
/my-app/$ npm install esbuild @babel/standalone
/my-app/$ npm install react react-dom
(バージョンを限定する場合は、react@19 react-dom@19 等と指定する)

インストールしたモジュールは ./node_modules/ に格納される。
1-3. React.js を作成する。
./node_modules/ から
react/package.json, react-dom/package.json (の client 節)
をインポートし、それぞれを React, ReactDOM として公開する。

/my-app/$ npx esbuild --bundle --minify --format=iife \
--outfile=React.js --define:process.env.NODE_ENV='"production"' \
--global-name=MyReact \
<< EOF
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
export { React, ReactDOM };
EOF

/my-app/$ ls -ogh React.js
-rw-r--r--. 1 189K  4月 14 22:31 React.js    
→ このファイルを hello.html の設置場所へコピーする。
RHEL 系なら scp。WSL2 なら cp React.js /mnt/c/Users/who/.
1-4. Babel を取り出す。
/my-app/$ cp node_modules/@babel/standalone/babel.min.js .
/my-app/$ ls -ogh babel.min.js
-rw-r--r--. 1 3.0M  4月 14 22:25 babel.min.js
→ このファイルを hello.html の設置場所へコピーする。


2. HTML の作成と表示

2-1. ReactDOM をテストする。
• hello.html を作成する。
 (上記 1-3,1-4 で作成した React.js, babel.min.js と同じ場所に作成する)
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
<!DOCTYPE html>
<body>
    <div id="msg" align="center">ここにメッセージが入る</div>

    <script src="./React.js"></script>
    <script>
        const { React, ReactDOM } = MyReact;  // ビルド時に指定した --global-name
        const mesg = React.createElement("h1", null, "Hello, React");
        const root = ReactDOM.createRoot(document.getElementById("msg"));
        root.render(mesg);
    </script>
</body>
</html>

• hello.html を Web ブラウザで開く。
 (以下は Firefox 149.0.2 で表示した例)
2-2. JSX をテストする。
• jsx.html を作成する。
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
<!DOCTYPE html>
<body>
    <div id="msg" align="center">ここにメッセージが入る</div>

    <script src="./React.js"></script>
    <script src="./babel.min.js"></script>
    <script type="text/babel">
        const { React, ReactDOM } = MyReact;
        const mesg = <h1 style={{ textAlign: "center" }}>Hello, JSX</h1>;
        const root = ReactDOM.createRoot(document.getElementById('msg'));
        root.render(mesg);
    </script>
</body>
</html>

• jsx.html を Web ブラウザで開く。
 (以下は Firefox 149.0.2 で表示した例)
2-3. useState をテストする。
• useState.html を作成する。
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
<!DOCTYPE html>
<body>
    <div id="msg" align="center">ここにメッセージが入る</div>

    <script src="./React.js"></script>
    <script src="./babel.min.js"></script>
    <script type="text/babel">
        const { React, ReactDOM } = MyReact;
        const { useState } = React;

        function App() {
            const [mesg, setMesg] = useState("Click the button.");

            return (
                <div>
                <h1>{mesg}</h1>
                <button onClick={() => setMesg("Left arrow was Clicked")}>&larr;</button>
                <button onClick={() => setMesg("Right arrow was Clicked")}>&rarr;</button>
                </div>
            );
        }

        const mesg = <App />
        const root = ReactDOM.createRoot(document.getElementById('msg'));
        root.render(mesg);
    </script>
</body>
</html>

• useState.html を Web ブラウザで開く。
 (以下は Firefox 149.0.2 で表示した例)
2-4. useEffect をテストする。
• useEffect.html を作成する。
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
<!DOCTYPE html>
<body>
    <div id="msg" align="center">ここにメッセージが入る</div>

    <script src="./React.js"></script>
    <script src="./babel.min.js"></script>
    <script type="text/babel">
        const { React, ReactDOM } = MyReact;
        const { useState, useEffect } = React;

        function App() {
            const [mesg,  setMesg ] = useState("初期値");
            const [count, setCount] = useState(0);

            useEffect(() => {setMesg(1 + count + "回目")}, [count]);

            return (
                <div>
                <h1>useEffect: {mesg}</h1>
                <button onClick={() => setCount(count + 1)}>Click Me</button>
                </div>
            );
        }

        const mesg = <App />
        const root = ReactDOM.createRoot(document.getElementById('msg'));
        root.render(mesg);
    </script>
</body>
</html>

• useEffect.html を Web ブラウザで開く。
 (以下は Firefox 149.0.2 で表示した例)
2-5. 自作フックをテストする。
• myHook.html を作成する。
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
<!DOCTYPE html>
<body>
    <div id="msg" align="center">ここにメッセージが入る</div>

    <script src="./React.js"></script>
    <script src="./babel.min.js"></script>
    <script type="text/babel">
        const { React, ReactDOM } = MyReact;
        const { useState } = React;

        function useSp(initVal) {
            const [count, setCount] = useState(initVal);
            const increment = () => { setCount(count + 1) };
            const decrement = () => { setCount(count - 1) };
            return { count, increment, decrement };
        }

        function App() {
            const sp1 = useSp(0);
            const sp2 = useSp(0);
            return (
                <div>
                    <div>
                    SP1: { sp1.count }  
                    <button onClick={sp1.increment}>+1</button>
                    <button onClick={sp1.decrement}>-1</button>
                    </div>
                    <div>
                    SP2: { sp2.count }  
                    <button onClick={sp2.increment}>+1</button>
                    <button onClick={sp2.decrement}>-1</button>
                    </div>
                </div>
            );
        }

        const mesg = <App />
        const root = ReactDOM.createRoot(document.getElementById('msg'));
        root.render(mesg);
    </script>
</body>
</html>

• myHook.html を Web ブラウザで開く。
 (以下は Firefox 149.0.2 で表示した例)
2-6. ファイル分割をテストする。
• myHookSp.js を作成する。
 (上記 2-5 からフック useSp() を切り出したもの)
1 
2 
3 
4 
5 
6 
function useSp(initVal) {
    const [count, setCount] = useState(initVal);
    const increment = () => { setCount(count + 1) };
    const decrement = () => { setCount(count - 1) };
    return { count, increment, decrement };
}

• myHookRoot.html を作成する。
 (上記 2-5 から useSp() を除外して <script src=…> を追加したもの)
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
<!DOCTYPE html>
<body>
    <div id="msg" align="center">ここにメッセージが入る</div>

    <script src="./React.js"></script>
    <script src="./babel.min.js"></script>
    <script src="./myHookSp.js"></script>
    <script type="text/babel">
        const { React, ReactDOM } = MyReact;
        const { useState } = React;

        function App() {
            const sp1 = useSp(0);
            const sp2 = useSp(0);
            return (
                <div>
                    <div>
                    SP1: { sp1.count }  
                    <button onClick={sp1.increment}>+1</button>
                    <button onClick={sp1.decrement}>-1</button>
                    </div>
                    <div>
                    SP2: { sp2.count }  
                    <button onClick={sp2.increment}>+1</button>
                    <button onClick={sp2.decrement}>-1</button>
                    </div>
                </div>
            );
        }

        const mesg = <App />
        const root = ReactDOM.createRoot(document.getElementById('msg'));
        root.render(mesg);
    </script>
</body>
</html>

• myHookRoot.html を Web ブラウザで開く。
 (ファイル分割しただけなので、表示・機能ともに上記 2-5 と同じ)