UIを更新するのにUI自体の要素は一切変更しない

これが宣言的UIと言われる考え方で最も重要なコンセプトですが、ここで詰まる人は必ずいます。特に初めてフロントエンドのフレームワークを使う方は。バックエンドの経験があっても、です。

直感的なUIの更新コードって、こうですよね

以下、jQueryとかでよく見るコードです。

<button id="click-button">クリックしてね</button>
<p>クリック回数: <span id="click-count">0</span></p>

  <script>
    $(document).ready(function () {
      let count = 0;

      $('#click-button').on('click', function () {
        count++;
        $('#click-count').text(count);
      });
    });
  </script>

ポイントは、click-count という要素を取得して、 textというUIの属性を書き換えていることです。UI(こここではspanタグを初期化した後に、そのタグの属性を書き換えています。UIが初期化した後でも、UIが持っている属性(ここではtext)を変更できます。色や表示/非表示等も、同様に変更できます。

Flutterに限らず、宣言的UIをサポートしているフレームワークでは、上記のようなコードは書けません。UIを初期化した後で、その属性を何かのイベントで書き換えることが出来ないんです。

宣言的UI a.k.a UI = f(state)

FlutterでUIを変更するには、「自分が参照しているデータは更新されたので、UIを再構築(リビルド)しなさい」とWidgetに伝える必要があります。

UIを更新するロジックを書くのではなく、カウントアップされたので新しい数字になったので、今までのUIは破棄して、全く新しいUIを作って表示を更新する、という考え方になります。

UIの属性(テキストやスタイル)を都度書き換えるスタイルは、UIのロジックとデータを読み書きするロジックが、どうしても混在します。UIの属性を直接書き換えるスタイルはわかりやすくはありますが、どこからでもUIが更新できるため、秩序が崩れやすいです。

それに比べ宣言的UIで定義するのは、状態です。状態を単純に言えば、ユーザー操作によって更新されるデータのことです。カウントアップだと、押された回数がそれに当たります。更新があるので、その都度、最新化する必要があります。

更新されない読み取り専用のデータであれば、状態は存在しません。常に同じものを出すだけ。カウンターのように、最新化する必要があるのであれば、最新の状態でUIを構築する必要があります。

この絵を丸暗記してください。経典のようなものです。

FlutterでUIを構成するパーツのことを、Widgetと呼びます。HTMLでいうと、タグです。pとかspanとかtableとか。上記の方式は、Flutterにおきかえると Widget = build(state) になります。stateを引数に入れてbuildメソッドが実行されます。

Stateって何

次の問題です。state は何を表現しているのかについて、です。

これについては、別の記事で詳しく取り上げます。