なぜ React を使うのか

Part 1 · React の世界観

なぜ React を使うのか

素の JavaScript で同じものを作って比べてみる

Web ページに動きをつけるだけなら、React は必要ありません。JavaScript だけでもボタンは押せますし、文字も書き換えられます。

では、なぜみんな React を使うのか。 その理由は画面が複雑になったときに何が起きるかを見ると分かります。

この章では、同じものを 2 通りの方法で作って比べます。

React を使わずに作ると

数字があって、ボタンで増やせて、リセットもできる。 それだけのカウンターを素の JavaScript で書くと、右のようになります。

注目してほしいのは render() という行です。値を変えたあと、毎回これを呼んでいます。

increment?.addEventListener("click", () => {
  count = count + 1;
  render(); // ← これを忘れると画面が変わらない
});

JavaScript は「count が変わったから画面も直そう」とは考えてくれません。値の変更と画面の更新は、まったく別の作業です。 だから人間が、変えるたびに手で呼ぶ必要があります。

これが破綻していく

カウンターひとつなら問題ありません。問題は、画面が現実的な規模になったときです。

  • 数字を表示する場所が 3 か所に増えた
  • 0 のときはリセットボタンを消したくなった
  • 10 を超えたら色を変えたくなった
  • 合計を別の場所にも出したくなった

そのたびに「値が変わる場所」を全部探し出して、更新の処理を書き足していくことになります。 1 か所でも書き忘れると、そこだけ古い表示が残ります

vanilla-counter.ts

/*
  React を使わずに、同じカウンターを作るとこうなる。
  「値を持つ」だけでなく「値が変わったら画面のどこを書き換えるか」まで
  自分で面倒を見る必要がある。
*/

let count = 0;

const label = document.querySelector("#count");
const increment = document.querySelector("#increment");
const reset = document.querySelector("#reset");

// 値を変えるたびに、表示の更新を自分で呼ぶ
const render = () => {
  if (label) label.textContent = String(count);
};

increment?.addEventListener("click", () => {
  count = count + 1;
  render(); // ← これを忘れると、値は増えているのに画面が変わらない
});

reset?.addEventListener("click", () => {
  count = 0;
  render(); // ← ここでも忘れてはいけない
});

render(); // 最初の表示も自分で行う

React で作ると

同じものを React で書くと、右のようになります。

React のカウンター

更新のための処理はどこにも書いていない

0

コードの中を探しても、render() にあたるものがありません。setCount(count + 1) と書いてあるだけです。

それでも画面は更新されます。React が「値が変わった」ことを検知して、勝手に描き直しているからです。

さきほど挙げた「表示場所が 3 か所に増えた」「0 のときはボタンを消したい」も、 React なら表示のしかたを書くだけです。 更新の処理を足す必要はありません。 書き忘れようがないので、あのバグ自体が起きなくなります。

react-counter.tsx

"use client";

import { Button } from "@/components/ui/button";
import { useState } from "react";

export function ReactCounter() {
  const [count, setCount] = useState(0);

  return (
    <div className="flex items-center gap-4">
      <p className="font-mono text-lg font-semibold tabular-nums">{count}</p>

      <Button size="sm" onClick={() => setCount(count + 1)}>
        増やす
      </Button>
      <Button size="sm" variant="outline" onClick={() => setCount(0)}>
        リセット
      </Button>
    </div>
  );
}

その代わりに払うもの

React はただの魔法ではありません。得るものがある代わりに、 いくつか引き受けなければならないことがあります。

  • React の作法(この教材でやること)を覚える必要がある
  • 「いつ描き直されるか」を意識しないと、遅くなることがある(Part 7 以降)
  • 値を書き換えるのではなく新しく作るという書き方に慣れる必要がある(Part 4)

小さなページなら、素の JavaScript のほうが手っ取り早いこともあります。 React が効いてくるのは、画面の状態が増えて、表示との対応が人間の手に負えなくなってからです。

react-counter.tsx

"use client";

import { Button } from "@/components/ui/button";
import { useState } from "react";

export function ReactCounter() {
  const [count, setCount] = useState(0);

  return (
    <div className="flex items-center gap-4">
      <p className="font-mono text-lg font-semibold tabular-nums">{count}</p>

      <Button size="sm" onClick={() => setCount(count + 1)}>
        増やす
      </Button>
      <Button size="sm" variant="outline" onClick={() => setCount(0)}>
        リセット
      </Button>
    </div>
  );
}

理解できたか確かめる

確認クイズ

素の JavaScript で画面を作るときにいちばん大変なのは?

確認クイズ

React を使うと何がなくなる?

react-counter.tsx

"use client";

import { Button } from "@/components/ui/button";
import { useState } from "react";

export function ReactCounter() {
  const [count, setCount] = useState(0);

  return (
    <div className="flex items-center gap-4">
      <p className="font-mono text-lg font-semibold tabular-nums">{count}</p>

      <Button size="sm" onClick={() => setCount(count + 1)}>
        増やす
      </Button>
      <Button size="sm" variant="outline" onClick={() => setCount(0)}>
        リセット
      </Button>
    </div>
  );
}

この章のまとめ

  • 素の JavaScript では、値の変更と画面の更新が別々の作業になる
  • そのため「値は合っているのに表示が古い」というバグが起きやすく、 画面が複雑になるほど増える
  • React はその対応づけを肩代わりする。 人間は値と表示のしかただけを書けばよくなる
  • 代わりに React の作法を覚える必要がある。それがこの教材

react-counter.tsx

"use client";

import { Button } from "@/components/ui/button";
import { useState } from "react";

export function ReactCounter() {
  const [count, setCount] = useState(0);

  return (
    <div className="flex items-center gap-4">
      <p className="font-mono text-lg font-semibold tabular-nums">{count}</p>

      <Button size="sm" onClick={() => setCount(count + 1)}>
        増やす
      </Button>
      <Button size="sm" variant="outline" onClick={() => setCount(0)}>
        リセット
      </Button>
    </div>
  );
}