👉

jQueryと比べるとReactの何がいいのか~命令的と宣言的~

2024/12/03に公開

こんにちは。あいおんです。

この記事の経緯・背景

少し前に、mizchiさんの「フロントエンドとJavaScriptの歴史を雑に話す会」を拝見しました。

https://x.com/mizchi/status/1805938378023608344

そこで、改めて「jQueryじゃなくてReactがあってよかったなぁ」という趣旨で以下のポストをしたところ、ご本人様からいいねをもらってしまったので震えました。

https://x.com/rudolf_aion/status/1809880792698618037

これは絶対書きたいな・・・と思ったのでここに表しておきます。

(日にちが空きすぎているのは…すみません…バタバタしてたんです…)

この記事の主訴

  • 宣言的であるReactのすばらしさを伝えたい
    • そもそも、私自身が「命令的の方がわかりやすいだろ」と思っていたので、なんで宣言的の方がいいと思うようになったかについて書きます
  • 歴史がわかっていないと、「宣言的」の良さがわからない
    • だからこそ、jQueryなどの命令的な書き方から考えていく
  • コード読めなくてもわかるようにする
    • 概念的なお話にしたいので、コードは書かない説明にしていきます。
  • 概念的なお話に綴じるので、わかりやすさ重視で
    • 「いやいやそれは違うよ」ということが出てきたらこっそり教えてください・・・

jQuery時代:命令的に処理していく

単純にデータを取得して表示させるだけ

さかのぼること2005年ぐらい。世はまさにAJAX全盛期
ウェブアプリとしては、APIサーバなどにデータが置いてあり、それを取りに行きそのデータをブラウザが表示するという形が多かった。

このやり方だと、まさに「もらったデータをそのまま一つずつ表示させていく」ということで、「命令的」が非常にしっくりくる。

だって、「〇〇というデータをとってきたから、そのままウェブページの要素として追加していく」というだけなので。

もらってきたデータをそのまま表示させるだけならこれが一番早いし、わかりやすいと思います。

そうなったときに、素のJavaScriptよりもjQueryのほうが便利だったんです。(コードを見たい人は以下。)

素のjsとjQueryのコードの比較
  • 素のjs
    • XMLHttpRequest関数を使う。
    • そのあとoutputのIDを持つ要素を取得する
    • outputを空にする
    • pの要素を作って要素を追加していく
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data', true);
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4 && xhr.status === 200) {
    var data = JSON.parse(xhr.responseText);
    displayData(data);
  }
};
xhr.send();

function displayData(data) {
  var output = document.getElementById('output');
  output.innerHTML = '';
  for (var i = 0; i < data.length; i++) {
    var item = document.createElement('p');
    item.textContent = data[i].name;
    output.appendChild(item);
  }
}
  • jQuery
    • ajax関数を使ってデータを取得する
    • outputのIDを持つ要素を取得
    • outputの中身を空にする
    • p要素を追加していく
$(document).ready(function() {
  $.ajax({
    url: 'https://api.example.com/data',
    type: 'GET',
    dataType: 'json',
    success: function(data) {
      displayData(data);
    },
    error: function(xhr, status, error) {
      console.error('Error:', error);
    }
  });

  function displayData(data) {
    var output = $('#output');
    output.empty();
    $.each(data, function(index, item) {
      $('<p>').text(item.name).appendTo(output);
    });
  }
});

両方ともかけるには書けますが、jQueryの方が「何をしたいか」に焦点が当たっており、理解しやすいコードですね。

ということで、AJAXを使ったウェブページはめちゃくちゃ先進的で、みんなやりたがったんですね。
で、それにうまく乗っかった(ハマった)のがjQueryだった、ということだと思っています。

ソートなどの処理を考える

では、ただ単に取得して表示するだけではなくソートするなどの「処理」を考えてみましょう。

実際に見えている要素としてソートを考えるのはちょっと大変すぎます。
ということで、一旦データをメモリ上に格納し、その中でソート処理などを実施し、そのうえで、その結果を画面描画をすればいい感じになりますね。

その場合はjQueryで実装する場合、以下の流れの処理になります。

  1. データをとってくる
  2. とりあえずもらった順に表示させる
  3. ユーザから操作があったら、一旦メモリに持ってくる
  4. そのうえでソート処理をする
  5. その結果をブラウザの今の表示をいったん消して全部表示しなおす

という感じですかかね。

見えている世界を正にするとしんどい

今のソートの処理でもわかったと思いますが、わざわざ「今見えている世界」を正にするとちょっと大変な部分があります。
ここで、今見えている世界を「物理の世界」とし、メモリ上の世界を「仮想の世界」とします。

物理の世界を基準とすると、一旦仮想の世界に一度全部持っていき、その中で計算、そして計算しなおした結果を、毎回物理の世界を全部一度壊して、物理の世界を作り直すことになります。

命令的なやり方では、作り終わった後の世界に関しては興味がなくなってしまうのです。
なので、何かの処理をしたいと思ったときには「すべて最初から命令をする」必要があるのです。

また、命令的な記載をしていくと、「物理の世界」と「仮想の世界」を一致させることを考えなくちゃいけなくなります。
つまり、先ほどの例でいうと「①ユーザの操作があったら一旦、物理の世界から仮想の世界に持っていき②仮想の世界で処理をした後に③仮想の世界を物理の世界に適用する」必要があります。これを自分で実装しなくちゃいけません。
②は処理の部分なので、ともかくとして①と③に関しては手間がすごいですね。

また、毎回全部一から描画していたらとてつもなく時間もかかりそうです。

Reactだと宣言的になる

ということで、jQueryを始めとした「命令的」にはたくさんの問題がありました。整理すると以下です

  • 今の状態がわからないので全部最初からの描画になる
  • 物理の世界と仮想の世界を自分でつながなくちゃいけない

開発者は「どういう表示であるか」という状態には興味がありますが、「どうやって表示させるか」という手段については興味がありません。

ということで、ここでReactが出てきて俺たちの魂を揺さぶったわけです。

Reactはどのように表示されていてほしいか、を定義します。
つまり、記載する内容は以下です。

  1. データを取得する
  2. データの要素ごと表示

はい、これだけです。
「操作」を定義するのではなく「状態」を定義することによりとても見通しがよくなった、というわけですね。

Reactで書いてみる
  • React
    • データを取得
    • そのデータごとにmapでpタグとして要素を表示させる
import React, { useState, useEffect } from 'react';

function DataDisplay() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then(data => {
        setData(data);
      })
      .catch(error => {
      });
  }, []);

  return (
    <div>
      {data.map(item => (
        <p key={item.id}>{item.name}</p>
      ))}
    </div>
  );
}

export default DataDisplay;

Reactを始めとした宣言的に定義ができるというのは、「状態」を定義するということです。

まとめ

  • jQuery:命令的
    • 作業を書いていくことになるので、わかりやすい。
    • 仮想の世界と物理の世界を自分でつなぐ必要がある。
  • React:宣言的
    • 仮想DOMを定義したらそれがそのまま物理DOMに反映される。
    • つまり、「どういう状態であるべきか」を宣言するもの。
       - あとは勝手にReact君が表現してくれる。
  • 技術者の興味があるのは
    • 「どう表示されているか(状態)」に興味があるだけで、「どう表示させるか(手段)」には興味がない。

・・・なんか書きたかったことと若干それてる気がします(仮想DOMのお話もあったので)が、言いたかったのは宣言的って素晴らしいということでした!

突っ込みあればコメントください!

Discussion