We might need to figure out how to better document this, but reducer ordering is very important, and the rule of thumb is you almost always want to run local reducers before more global ones.
In your example it is tempting to have a local close
action that a parent hooks into but does nothing on its own in the child reducer. While this seems perfectly reasonable, and makes it sound like the order of the combine
doesn't matter, if you come back to this code later and try to instrument it so that the local reducer fires off an effect when it receives close
you will have a bug on your hands if the global reducer runs first:
- Global reducer intercepts
.close
andnil
s out state - Local reducer cannot run with optional state so
.close
logic is never run
This is why we prefer to crash here and not silently ignore actions sent to optional reducers, because it almost always means that there's a bug just waiting to happen. For instance, in the thread mentioned above, the precondition caught the fact that timer cancellation was not actually happening.
Also in that thread @mbrandonw has shared a higher-order reducer that may make it easier to reason about these kinds of onAppear
and onDisappear
lifecycle-y reducers: IfLetStore and Effect cancellation on view disappear - #2 by mbrandonw