Skip to content

Latest commit

 

History

History
801 lines (586 loc) · 30.2 KB

File metadata and controls

801 lines (586 loc) · 30.2 KB
title React

import createReactProjectVideo from "./create-react-project.mp4"; import componentRenderingVideo from "./component-rendering.mp4";

宣言的なUI

これまで、JavaScriptによりHTML要素を操作するために、DOMを用いることができることを学んできました。しかしながら、ナイーブな方法によりDOMを使用すると、アプリケーションの規模の限界がすぐにやってきます。

簡単なToDoアプリケーションを例に考えてみましょう。

<ul id="todos"></ul>
<input id="message" />
<button id="add-todo" type="button">追加</button>
const todoContainer = document.getElementById("todos");
const messageInput = document.getElementById("message");
const addTodoButton = document.getElementById("add-todo");

addTodoButton.onclick = () => {
  const message = messageInput.value;
  const li = document.createElement("li");
  const span = document.createElement("span");
  span.textContent = message;
  li.appendChild(span);
  const removeTodoButton = document.createElement("button");
  removeTodoButton.type = "button";
  removeTodoButton.textContent = "削除";
  removeTodoButton.onclick = () => {
    todoContainer.removeChild(li);
  };
  li.appendChild(removeTodoButton);
  todoContainer.appendChild(li);
};

なんとか作り上げることができましたが、このまま要件を増やして複雑なプログラムを作ろうとすれば、要素の作成忘れ、削除忘れなどにより、すぐに破綻してしまいそうです。

このようになってしまう根本的な原因は、現在の状態がDOMに記憶されてしまっていることにあります。DOMには、テキストや位置、色など、数えきれない状態が格納されています。状態の数が$n$種類あれば、その遷移パターンは$n^2$個になるわけですので、状態の数が増えることが非常に危険であることは明らかです。

ところが、アプリケーションの本質的な状態というのは、一般的にそこまで多いものではありません。例えば、ToDoリストアプリケーションであれば、各ToDoを表すstringの配列string[]がひとつだけあれば、アプリケーションの状態は全て表現できていることになるはずです。

宣言的UIは、こういった性質に着目します。より具体的に説明するのであれば、アプリケーションの状態$S$に対し、関数$f(S)$によりUIの状態を表現できるのであれば、開発者の関心を$S$の変化と$f$の定義のみに絞ることができるというわけです。

具体的なコードで確認してみましょう。先ほどのToDoアプリケーションを、宣言的UIのアプローチを用いて書き換えてみましょう。状態を追いやすいよう、TypeScriptを用いて記述します。

まずはアプリケーションの状態と、その状態を格納する変数を宣言します。

type State = { todos: string[] };
const state: State = { todos: [] };

続いて、state変数をもとにUIを構築する関数renderを定義します。

function render() {
  // いったん既存の要素を全て削除
  todoContainer.innerHTML = "";

  for (const todo of state.todos) {
    const li = document.createElement("li");
    // ここにliの中身を作る処理が入る
    todoContainer.appendChild(li);
  }
}

最後に、状態を変化させる関数を定義します。状態を変化させたら、render関数を呼び出して、変更をUIに反映させます。

function addTodo(todo: string) {
  state.todos.push(todo);
  render();
}
function removeTodo(index: number) {
  state.todos.splice(index, 1);
  render();
}

これにより、アプリケーション全体の状態が変数stateに集約され、開発者が意識すべき状態のパターンを大幅に減らすことに成功しました。

React

先ほどのプログラムはうまく動作しますが、一つ問題があります。それは、render関数が呼ばれるたびに全ての要素が削除され、再構築される点です。一般的に、DOMに対する操作は非常にコストが高く、可能な限り減らすことがパフォーマンス改善のために有効です。

Reactは、この問題を仮想DOMを用いて解決します。Reactは、DOMに似たデータ構造を内部的にJavaScriptオブジェクトの形式で保持し、実際に変更された部分のみを実際のDOMに反映させることで、高いパフォーマンスを実現しています。

それでは、Reactを用いたプロジェクトを作成してみましょう。Viteでプロジェクトを作成しますが、frameworkReactvariantTypeScriptを選択してください。