Edited at

reduxでのstate管理について

More than 3 years have passed since last update.

reactとreduxで新製品を開発しております。reactはおろかフルスクラッチの設計を担当するのも初めてだったので、色々と試行錯誤の毎日です。現状ベストとはとても思えない設計ですが、ある程度軌道には乗りました。そこで今後のためにも一旦、試行錯誤した点をまとめておこうと思い記事を書いてみることにしました。

試行錯誤した点はかなりあるのですが、今回はreduxのstate管理について書いてみようと思います。

書く必要もないかもしれませんが基本的にreduxのコンセプトは複数のstateから1つの大きなstateを形成して、そこで状態を管理するというものです。ソースでいうと下のような形になります。


rootReducer.js

// fromのディレクトリ構成は適当です

import xxx from 'xxx';
import yyy from 'yyy';
import zzz from 'zzz';

const rootReducer = combineReducers({
xxx,
yyy,
zzz,
(以下略)
});


上記のような形でstateを追加していくと以下の2点問題が発生しました。


  • 1,2種類しかstateが存在しない非常に小さなaction,reducerのファイルが数多くできてしまった

  • 1つのIdに紐づくstateが大量に作成された結果、巨大なaction,reducerファイルができてしまった

今回は1つ目について書いていきたいと思います。私なりのこの問題への結論は

component間で共有するstateはreduxを使って管理し、component内で完結するstateはcomponent内で管理するということです。そうすればaction、reducerのファイルを減らせるしコード量も減るし管理も楽になると考えました。

問題についてもう少し説明させてください。


sideBar.js

// stateの初期値はinitialStateというファイルですべて宣言しています

import initialState from 'initialState';

export default function sideBar(state = initialState.sideBar, action) {
switch (action.type) {
case ActionTypes.OPEN_SIDE_BAR:
return update(state, {
$merge: {
isOpenedSideBar: !state.isOpenedSideBar
}
});
default:
return state;
}
};


上のソースはサイドバーの表示非表示のstateを変更するreducerです。(今回の製品にはサイドバーと呼ばれるものがあります。サイドバー内でイベントを発火してダイアログ表示など別の処理を呼び出します) 非常に単純な処理です。こんな簡単な処理を行うreducerが結構な数作成されてしまいました。

ある程度reducerのファイルが増えても大して問題ないですが、このレベルのstateでreducerに分けていたらファイルが再現なく増えてしまいます。場合によっては数年にわたり機能拡張をしていくことになるわけですから、無視してよい問題のようには思えません。

reduxの作者の考えとは異なりますが、場合によってはreactのcomponentにstateを持たせてしまってもよいのではないでしょうか。そもそもreduxでstateを管理すると具体的に何が嬉しいのか考えてみました。私が思いついたのは以下の3つです。


  • 1.componentがstateの内容を知る必要がなく、どこからでも好きなようにactionを使ってstateを変更できる

  • 2.stateの管理がaction,reducerに固まるので管理が簡単

  • 3.stateを管理するaction、reducerともに単なる関数なのでテストが簡単

component内部でstateを管理した場合どうなるでしょうか。それでは1つずつ考えていきたいと思います。

1.componentがstateの内容を知る必要がなく、どこからでも好きなようにactionを使ってstateを変更できる

 component間で共有するstateだと非常に面倒くさそうです。共通の親componentにstateを持たせて、各componentまでpropでstateを渡して やる。まるでバケツリレー。勘弁願いたいです。素直にreduxで管理してconnect使ってsmart component作りましょう。逆にcomponent内でstateが完結するなら、1は特筆すべきメリットにならないので気にする必要はないかなと思います。 少なくとも例で挙げたようなサイドバーの開閉の状態ぐらいであれば、component内でstateを持ってしまってもよいように感じます。

  

2.stateの管理がaction,reducerに固まるので管理が簡単

 上記のような形になる以上、component間で共有するstateだと管理が煩雑になってしまいそうです。上手くstateを設計すれば管理は煩雑にならないのかもしれませんが、そんなに頑張らなくても素直にreduxに任せればよいのではないかと思います。反面component内で完結するstateなら問題ないと思われます。componentのjsxファイル一つ開けばすべて完結するわけですから、reduxを使うよりも簡潔になっていますね。

  

3.stateを管理するaction、reducerともに単なる関数なのでテストが簡単

 これはどちらもそんなに面倒にならないのでは?と感じました。component間でstateを共有する場合、stateを変更するメソッドは親componentが持つことになると思います。stateを変更メソッドをinstanceメソッドにして、それをテストすれば終了。あまり問題なさそうです。component内で完結するstateも同様ですね。

以上のようなことから、上で述べたように

component間で共有するstateはreduxを使って管理し、component内で完結するstateはcomponent内で管理するようにすれば、小さなaction,reducerファイルが乱立するような問題は解決できるのではないかと思いました。

今回開発していて問題になったのはactionとreducerのファイル数だけですが、component内でstateを持つことには他にも意味があるように開発をしていて思いました。例えばcomponent内でstateを管理すれば、コーディングの記述量もreduxと比べると減りますし、componentの切り替えも容易になります。UIプロトタイピングとかABテストとかしたい場合は有効なのではないでしょうか。

  

ではreduxでの管理とcomponent内部での管理を混ぜてみたらどうなるでしょうか?「このcomponentに関わるstateが知りたいけど、どこ見れば良いんだよ。」ということになりかねない気がします。開発チームから保守チームに移った際など、stateの概要が分かりにくく問題になりそうです。1つのcomponentを管理するstateをreduxで管理するか、component内部で管理するかというのはしっかりと切り分けた方が良いように思います。

しかしまぁ見事に文字だらけになってしまいました。コードが恋しいですね。次回はもっとコードを書き込むことにします。