こういうコード書いて、末端コンポーネントで直接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:じゃっかんのごへいはありますが