@@ -9,7 +9,7 @@ import componentRenderingVideo from "./component-rendering.mp4";
99
1010これまで、JavaScriptによりHTML要素を操作するために、[ DOM] ( /docs/trial-session/dom/ ) を用いることができることを学んできました。しかしながら、ナイーブな方法によりDOMを使用すると、アプリケーションの規模の限界がすぐにやってきます。
1111
12- 簡単なToDoアプリケーションを例に考えてみましょう。
12+ 簡単なToDoアプリケーションを例に考えてみましょう。これまで学んできたDOM操作による処理の記述は、 ** 命令的アプローチ ** と呼ばれます。
1313
1414``` html title="index.html"
1515<ul id =" todos" ></ul >
@@ -49,6 +49,23 @@ addTodoButton.onclick = () => {
4949
5050** 宣言的UI** は、こういった性質に着目します。より具体的に説明するのであれば、アプリケーションの状態$S$に対し、関数$f(S)$によりUIの状態を表現できるのであれば、開発者の関心を$S$の変化と$f$の定義のみに絞ることができるというわけです。
5151
52+ <div style = { {textAlign: ' center' }} >
53+
54+ ``` mermaid
55+ flowchart LR
56+ add(["add"]):::event --> S["S"]:::state
57+ remove(["remove"]):::event --> S
58+ S --> f["f(S)"]:::ui
59+
60+ classDef event fill:#e8e8e8,stroke:#999,font-size:12px
61+ classDef state fill:#4a90d9,stroke:#2e6bb0,color:#fff
62+ classDef ui fill:#f5f5f5,stroke:#ccc
63+ ```
64+
65+ </div >
66+
67+ 開発者はイベント(` add ` や` remove ` )による状態の変化と、状態に対応するUIを定義します。UIの更新処理、つまり具体的なDOM操作は自動で行われます。
68+
5269具体的なコードで確認してみましょう。先ほどのToDoアプリケーションを、宣言的UIのアプローチを用いて書き換えてみましょう。状態を追いやすいよう、TypeScriptを用いて記述します。
5370
5471まずはアプリケーションの状態と、その状態を格納する変数を宣言します。
@@ -94,7 +111,15 @@ function removeTodo(index: number) {
94111
95112先ほどのプログラムはうまく動作しますが、一つ問題があります。それは、` render ` 関数が呼ばれるたびに全ての要素が削除され、再構築される点です。一般的に、DOMに対する操作は非常にコストが高く、可能な限り減らすことがパフォーマンス改善のために有効です。
96113
97- [ React] ( https://ja.reactjs.org/ ) は、この問題を** 仮想DOM** を用いて解決します。Reactは、DOMに似たデータ構造を内部的にJavaScriptオブジェクトの形式で保持し、実際に変更された部分のみを実際のDOMに反映させることで、高いパフォーマンスを実現しています。
114+ [ React] ( https://ja.react.dev/ ) は、この問題を** 仮想DOM** を用いて解決します。Reactは、DOMに似たデータ構造を内部的にJavaScriptオブジェクトの形式で保持し、実際に変更された部分のみを実際のDOMに反映させることで、高いパフォーマンスを実現しています。
115+
116+ :::tip[ 仮想DOMと差分検出]
117+
118+ Reactでは、開発者は「UIがどうあるべきか」を宣言するだけで済みます。状態が変化すると、Reactは新しい仮想DOMを作成し、以前の仮想DOMとの差分(diff)を自動的に計算します。そして、実際に変更が必要な部分のみをDOMに反映させます。
119+
120+ つまり、命令的なDOM操作を開発者の代わりにReactが行ってくれるのです。
121+
122+ :::
98123
99124それでは、Reactを用いたプロジェクトを作成してみましょう。Viteでプロジェクトを作成しますが、` framework ` は` React ` 、` variant ` は` TypeScript ` を選択してください。
100125
@@ -137,7 +162,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
137162
138163:::tip[ Non-null assertion operator]
139164
140- ` document.getElementById("root") ` の直後に続く` ! ` 記号は、TypeScriptのnon-null assertion operatorです。` document.getElementById ` 関数は、要素が見つからなかった場合に` null ` を返すため、戻り値は` HTMLElement | null ` 型と定義されています。` null ` である可能性がないことをプログラマが保証することをTypeScriptに伝える記号が ` ! ` です。なお、` tsconfig.json ` の設定によってはこのエラーは表示されません。
165+ ` document.getElementById("root") ` の直後に続く` ! ` 記号は、TypeScriptのnon-null assertion operatorです。` document.getElementById ` 関数は、要素が見つからなかった場合に` null ` を返すため、戻り値は` HTMLElement | null ` 型と定義されています。` null ` である可能性がないことを開発者が保証することをTypeScriptに伝える記号が ` ! ` です。なお、` tsconfig.json ` の設定によってはこのエラーは表示されません。
141166
142167``` typescript
143168document .getElementById (" root" ).textContent ; // Object is possibly 'null'.
@@ -146,6 +171,12 @@ document.getElementById("root")!.textContent; // OK
146171
147172:::
148173
174+ :::tip[ React.StrictMode]
175+
176+ ` <React.StrictMode> ` は開発環境でのみ有効になるモードで、潜在的なバグを検出するためにコンポーネントを2回実行します。これにより、コンポーネントが[ 純粋関数] ( https://react.dev/learn/keeping-components-pure ) であるかどうか(同じ入力に対して常に同じ出力を返すか)をチェックできます。本番ビルドには影響しません。
177+ StrictModeのため、開発環境では` console.log ` でデバックする際に2回実行されることに注意してください。
178+ :::
179+
149180それでは、` App.tsx ` を書き換えながら、Reactの動作を確認していきましょう。まずは、` App.tsx ` を次のように修正します。
150181
151182``` tsx title="App.tsx"
0 commit comments