• 作成:

勘違いしていましたがReduxのstateにはMapなどのプレーンじゃないオブジェクトも保存できます

致命的な勘違いをしていました

ReduxのstateにはJavaScriptのPlain Objectしか保存できないと思い込んでいました. ここでいうプレーンなオブジェクトというのはconstructor[Function: Object]になっているものですね.

正しい認識

結論から言うとReduxのstateには基本的に全てのデータ型のオブジェクトを格納できます. しかし一部はReduxの開発者ツールには表示されません.

実験

stateのfooフィールドにDate, moment, Immutable.Record, Mapを入れてconsole.logで表示しました.

すると, 全てのデータ型でオブジェクトは正常に型を保ったまま表示されました.

しかし, Mapのみ開発者ツールに{}として表示されました.

これは以下の結果を見れば条件がわかります.

> JSON.stringify(moment())
'"2018-08-18T03:30:43.933Z"'
> JSON.stringify(immutable.Map([['x', 1]]))
'{"x":1}'
> JSON.stringify(new Map([['x', 1]]))
'{}'

Reduxの開発者ツールはデータをJSON.stringifyで文字列化して表示するので, Map{}になってしまうというわけですね.

勘違いした原因

昔ReduxのstateにMapを使おうとして, 開発者ツールに表示されないから保存されていないと思いました.

その時にTwitterで混乱を呟いていたところ, redux/isPlainObject.js の存在を教えてもらって, ReduxのstateにはPlain Objectしか保存できないと思い込みが確定しました.

この思い込みをベースに, Immutable.jsのRecordをstateに正常に入れるにはどうすれば良いのか延々調べていたので笑えますね… そのまま使えるのに.

isPlainObjectを実行するとわかるのですが, これはMapもDateもnumberもfalseになるのですよね.

> isPlainObject(new Map())
false
> isPlainObject(moment())
false
> isPlainObject(new Date())
false
> isPlainObject(1)
false

数値がPlain Objectじゃないけど保存できる時点で気がつくべきでした.

この関数の使われている場所を見る限りこれは多分Actionのチェックです.

redux/createStore.js#L166

実際ActionはPlain Objectじゃないとダメですね.

保存できても表示されないのは実際困るから使わないでしょう

正常に保存利用出来ることはわかりましたが, Reduxの開発ツールで表示されないのは実際不便なのでstateにMapを使うことはないでしょう. Mapは単なるobjectに比べて反復が簡単とかキーに文字列以外を使えるというメリットがあるので, 出来れば使いたいのですが…

まあ一番良いのはImmutable.jsのMapを使うことですね. 分割代入できないのは玉に瑕ですが, デシリアライズする時以外はImmutable.jsは普通に使えることが分かってきたので, 新規プロダクトではどんどん使っていこうと思います. 新規プロダクトが無いのが問題なのですが…