「状態管理」って何?

2020/09/29に公開
1

状態管理ってなに??

フロントエンド界隈でかなり頻繁に飛び交うワード「状態管理」。
しかし、状態管理とは何か。正直「全然わからん」な方って結構いらっしゃるのではないでしょうか。

状態管理とはフロントエンド特有の特殊な事情から生まれたワードであり、設計論的なテーマなのでフロントエンド初心者、特に駆け出しエンジニアの方々にはえらく取っ付きにくい印象を与えてしまいます。
今回はそんな方々に分かりやすく「状態管理」について説明していこうと思います。

対象読者

状態管理って言われてピンとこないエンジニア

TL,DR

「状態管理」とは、SPA開発においてステートレスにUIを実装する上で必ず発生する 「では、どこに・どうやって状態を持つのか?」 というテーマのことです。(あくまで筆者の解釈です。)

SPA開発における複雑さは「状態」によって生まれる

従来のフロントエンドの主流はデータを静的なHTMLに変換するだけの「シンプルなお仕事」だった

一昔前、Viewの構築にはテンプレートエンジン(jspやBladeなど)と言われる技術が使われるのが一般的でした。
彼らの仕事はというと、与えられたデータを繰り返しや条件分岐、変数埋め込みを駆使してHTMLに変換することです。

@foreach($users as $user)
<div>
  {{$user->getUsername()}}
<div>
@endforeach

上記のBlade(Laravel)の例でいうと$userを引数に受け取って@foreachで回し、{{}}のなかに変数を埋め込むことでUIを構築しています。
シンプルですね。(バックエンドのMVCフレームワークごとに異なるテンプレートエンジンの記法を習得しなければならないという問題を除けば)
なぜならUIの構築が データとテンプレートを入力 として HTMLを出力 するだけの 関数的な 処理のみで完了してしまうからです。

ユーザーのブラウザ上で動的にUIが変わり続けるSPA

さて、昨今のフロントエンド開発のトレンドはSPA(シングルページアプリケーション)です。
それは従来の 関数的な UI構築とは違い、ユーザーのブラウザ上で動的にUIが変わり続ける 「UIの移り変わり」 の問題に開発者を直面させました。
例えば、UIの遷移は以下のようなケースで発生します。

  • データをサーバーから非同期で取得する
  • フォームのリアルタイムバリデーション
  • ページ内タブの切り替え

「UIの移り変わり」は何故複雑さを生むのか

では「UIが動的に変わり続ける」ことによってなぜ複雑になるのでしょうか。
それは「UIを変化させるには1つ前のDOMの状態に手をくわえ続ける」必要があるからです。
例としてバニラJSでデータをサーバーから非同期で取得して表示するサンプル実装を見てみます。

// APIからデータを取ってくる
const getTodoList = async () => {
  return ["遊ぶ", "買い物", "宿題"];
};

const container = document.getElementById("todo-list");

// ローディング画面を表示
const loading = document.createElement("div");
loading.textContent = "loading";
container.appendChild(loading);

getTodoList().then(todoList => {

  // ローディング画面を削除
  container.removeChild(loading);

  todoList.forEach(todo => {
    const dom = document.createElement("li");
    dom.textContent = todo;
    container.appendChild(dom);
  });
});

面倒なのはローディング画面から実際のTODOリストを表示する際に、ローディング画面を削除しなければならないことです。またTODリストを<div id="todo-list" />に表示させていますが、<div id="todo-list" />がDOM上にある保証はありません。SPA開発ではUIは多くの非同期処理によって常に目まぐるしく変化することが前提としてあるからです。
「1つ前のDOMの状態に手をくわえ続ける」ことは、DOMの足し算や引き算を強制させます。それはUIとロジックが密結合な状態を生み出すことになるのです。
これはJQueryなどの単なるDOM操作ヘルパーを用いても同じです。

フロントエンドはその複雑さをマークアップから完全に分離することに成功した

この複雑さを解決したのがReact(等の昨今のViewライブラリ)です。
Reactはコンセプトに Declarative(宣言的) を掲げ、(https://reactjs.org/)
UIの構築を以下の例ような「データをHTMLに変換する関数」として実装することを可能にしました。


// APIからデータを取ってくる
const getTodoList = async () => {
  return ["遊ぶ", "買い物", "宿題"];
};

// カスタムフック
const useTodoList = () => {
  const [state, setState] = React.useState<string[] | null>(null);
  React.useEffect(() => {
    getTodoList().then(todoList => {
      setState(todoList);
    });
  }, []);
  return state;
};

function App() {
  const state = useTodoList();
  // データがまだ取得できていないならローディング、取得できているならそれを表示する関数
  return <ul>{state ? state.map(e => <li>{e}</li>) : "Loding"}</ul>;
}

宣言的なUI構築って?

Reactが掲げるUI構築における「宣言的」とは何でしょう。先にも述べたように、「データをHTMLに変換する関数」として実装すること、と解釈することができます。
つまり、マークアップをする際には、引数に与えられた今・現在のデータをHTMLとしてどのように表現するのか。のみにフォーカスして実装を行えば良くなり、過去にDOMがどのような状態だったのか、という「UIの移り変わり」を気にする必要がなくなりました。

問題は「UIの移り変わり」から「データの移り変わり」に。そして「状態管理」というワードへ

マークアップは「UIの移り変わり」から解放されより簡単化されました。

しかしながら、それは 「マークアップ」 から 「状態」 を切り離しただけであり フロントエンドが「状態」から解放されることはありません。 「データの移り変わり」には相変わらず悩まされることになります。
フロントエンドの界隈はその問題を 「状態管理」 というワードでテーマ化し、そしてFluxやRedux等の状態管理のための道具立てが誕生することとなったのです。

クソみたいな図.jpg

「状態」を管理することの意義

もちろん「状態管理」というワードを知らなくても、状態管理ライブラリを入れなくてもReactやVueを使った開発は可能です。
しかしながら複数人規模の開発で状態管理についてアプリケーションで一貫とした方針が無ければ、開発者は「状態の管理」について1から頭を抱えることになりますし、
出来上がるソースコードは状態のコードとマークアップのコードがごちゃ混ぜの全容が見えにくいものになるでしょう。
反対に、もしあなたが状態を完璧に管理することができれば、ユーザーとドメインのコミュニケーションをあなたの意のままに操ることができるでしょう。
なぜなら状態とは、アプリケーションからユーザーが得る情報の「源泉」であり、 ユーザーにとってのたった一つの真実(Single source of truth) だからです。

Discussion

______

なるほど。良い記事でした。ありがとうございます!
jQuery が衰退して、React等が台頭してきたのは
view の生成を宣言的・関数的に行えることなんですね。