LoginSignup
3

More than 5 years have passed since last update.

複数の作用がセットのAction発行とどう向き合うか

Last updated at Posted at 2016-12-18

Redux を仕事と個人で使い始めて 3・4 週間という経験値なのですが、「唯一の状態である state の唯一の更新手段である Action 発行」が、更新以外の作用を伴うことに頭を悩ませていて、この点についてのお話をさせてもらいたいと思います。

なお、前提とする Redux ミドルウェアや周辺ライブラリは、公式推奨である以下です。

Action 発行に伴う 3 つの作用

Redux の Action 発行(= dispatch の実行)は、以下の 3 つの作用をセットで実行する処理だと、自分は考えています。

  1. state の更新をする。
  2. state のコピーをする。1
  3. 描画を実行する。つまり React の render 関数を実行する。2

それらの作用は常に全てが必要ではない

それぞれは勿論必要な処理ですが、常にセットで必要とは限りません。

3 が困る場合

単純に、描画に関係のない状態の更新であれば、実行をしない選択が出来る方が有り難いです。

React 側の「同期的な描画関数の実行は一回にまとめてくれる機能」を前提としても、例えば「60 fps でゲームプレイ時間を更新する」という処理がある場合、各フレームの処理は非同期であり、それぞれで描画が実行されてしまいます。

その場合は、「表示時間は秒単位だから、秒が繰り上がるときだけ描画する」ような調整をしたいです。

2 が困る場合

描画エンジン側が state に対して持つ参照の期間が不明な点を考慮するなら、一般的なプログラミングのルールとしては適切だと思います。

ただ一方で、巨大なオブジェクトや配列がそこに含まれる可能性もあり3、その際に、state の更新に対して都度変数のコピー処理が実行される仕組みは、規模の想定ミスやコーディングのイージーミスから大きな問題につながることが予測され、この点は良いことではありません。

これが Redux 内で必須の作法だとされているのは、3 のために都度描画関数に投げるためだと思ってますが、そもそもその 3 が要らないのであれば両方不要になります。

なお、1 だけを実行したい場合とは

ということで、もし 1 の state の更新を行いたいだけの場合も、2・3 の作用も発生してしまいます。

state の更新のみをしたい場合は数多くあり、先程のタイマーの例のように内部状態の変更が出力の変更にならない状況や、その他、時間の掛かる処理(Web-API アクセスなど)の結果を予め実行して保持して置きたい場合などがあります。

現在どんな Action / Reducer 設計にしているか

そのような Action の仕組みを受けて、個人的に今どういう設計にしているかです。

Reducer はいわゆる「リソース」別に定義しているとは思いますが、基本的にそのスコープが許す全体を何も考えずに更新する Action を一つ定義して、ほぼそれだけしか定義しません。
例えば、配列なら UPDATE_FOOS 、オブジェクトなら EXTEND_FOO のような感じです。

これは、Action の実行回数を減らすため、もしくは書き直しの手間を省くためです。
わかり難いので例を書くと、例えば、配列のある 1 行を更新する UPDATE_ROW を定義してしまったとして、その後もし複数行の更新が必要な Action が必要になった場合に、前述の理由で UPDATE_ROW をループして dispatch する処理は不適切なことが多いです。
その際に UPDATE_ROWS を別に作るくらいなら、最初から UPDATE_ROWS を定義してしまえ!という判断でした。

必定、その Action が取りうる引数の範囲は広くなるため、state を守るためのバリデーション処理や変換処理などが別途大量に必要になりますが、それは適当にオレオレモジュールを作り、Action Creator 内で呼び出して全てを適切に処理します。

Reducer 側で何もしないので、処理の本体はそのオレオレモジュール群の集合となり、一体 Redux とは何だったのか・・・4という有様です。5

加えて、そのオレオレモジュール群ですが、state がプレーンオブジェクトであるべきという制約上、クラスインスタンスで状態の保持ができないので、「第一引数に、ある決まったスキーマのプレーンオブジェクトを要求して計算して返す」ような表現の関数だらけになります。
JSON スキーマ大好きな人は幸せですが、自分はそうではないので単なる縛りプレイです。

以上

自分の Redux の理解がどこか間違ってるといいなって思って書きました。
色々ミドルウェアを使う前に生 Redux の力を知りたいです。6



  1. Redux 実装上の制約があるわけではありませんが、Reducers に "We don't mutate the state" 以下の様に記載されているので、必須の処理だと考えられます。 

  2. Usage with ReactExamples (特に React を使わない counter-vanilla)から、dispatch 時に render するのが意図した設計だろうと考えました。 

  3. 最大で Reducer で分割された範囲内だけのコピーだとしても、です。 

  4. 当記事公開から 2 週間実装を積み重ねた今の感想ですが、この通りに Reducer は単なる setter でありその処理や判断はしない、という設計がしっくり来るような気がしています。(※個人の感想です) 

  5. redux-saga などの Action を別用途に使うミドルウェアもありますが、そこでオレオレモジュールを呼び出すだけなのであんまり変わらなさそうです。 

  6. 趣旨から外れるので書きませんが、「初期状態の定義場所が一箇所に明確に限定される」など良いと感じた点もあります。 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3