こういうコード書いて、末端コンポーネントで直接stateの変化をsubscribeしようとした
class Logs extends React.Component { render() { return <ul> {this.props.logs.map((log) => { return <li>{JSON.stringify(log)}</li> })} </ul> } } Logs = connect((state) => { return { logs: state.logs } })(Log) export default Logs
Actionが発行されて、Dispatcherに通って、Dispatcherでは新しい値が来てるように見えてるんだけど、このpropsの更新に起因する再度のrenderには入っていない。
調査
mapStateToProps は、Providerだか大親コンポーネントのstateをconnectしたこのコンポーネントのpropsにmapするわけだけれど、とりあえずここにdebuggerを仕込む
Logs = connect((state) => {
+ debugger
return { logs: state.logs }
})(Logs)
export default Logs
そうすっと、 Connect の updateStatePropsIfNeededといういかにもそれらしいところでそれっぽい挙動がひっかかった

ソースコード
現在自分が持っているthis.statePropsと、新しく降ってきたnextStatePropsを比較して、差分が無ければ何もせずfalse、差分があれば置き換えてtrueを返す。
問題は、この、this.statePropsとnextStatePropsが等しいので、falseを返してる。なぜ。
this.stateProps、お前いつの間にnextStateProps先取りしてるんだ...
といった気持ちです。
原因:Reducerでのstateの扱いの注意
let store = createStore((state = {position:{top: 0, left: 0}, logs: []}, action) => { let logs = state.logs // <- ここと logs.unshift(action) // <- ここ switch (action.type) {
dispatcherに来るstateはcurrentStateで、ここでArrayやObjectのようにデフォルトで参照を渡す*1ものを直接どっかに置いてそれをいじっても参照元のcurrentState(ただしくはthis.statePropsになるわけだけど)も一緒に変わっちゃってるので、この後のupdateStatePropsIfNeededで、no needな状態になっちゃうというわけ。なので、以下のように変更。
let store = createStore((state = {position:{top: 0, left: 0}, logs: []}, action) => {
- let logs = state.logs
+ let logs = state.logs.slice(0)
logs.unshift(action)
switch (action.type) {
雑感
- react-redux、さいしょ敷居高いなーと思ってたけど、クセさえ覚えれば良いものっぽい
DRY
*1:じゃっかんのごへいはありますが