This section assumes you know how to create actions.
createReducer(...) export is the counterpart to
createAction(...). It takes an initial state and a list of action handlers. We'll see how to define those in a moment.
I'm sure you can guess at the first parameter, it just describes the reducer's initial state. The second parameter is where all the magic happens. It describes how that initial state changes over time by subscribing to a list of events (actions) and updating the model in response (reducers).
You can subscribe to actions with the
handleAction(...) callback. It ties a reducer to an action creator, so whenever that action gets dispatched, your handler is invoked. Any state changes in the handler are immediately applied.
The above example only changes state, but it also receives an action
payload. You may be used to receiving the entire action when writing reducers - be aware the only the payload is provided. This is quite intentional. Your reducer should never depend on
action.meta (that's the realm of middleware) and the action type is implied. What remains is the payload.
Immutably updating state has historically been quite difficult, inefficient, and error prone. You end up making more changes than you intended, you may accidentally mutate state, and many libraries are obtusely verbose.
Immer makes state changes far more robust (and type safe!) by modeling changes as mutations on draft states. The library has quickly gained widespread adoption and official support by the Redux team. If you're unfamiliar with immer, take a moment to check it out.
Retreon automatically wraps all your reducers with immer. It's safe and encouraged to use mutation syntax.
It may look jarring, but you'll get used to it very quickly.
If your action creator throws an error, retreon will dispatch an error action. You can listen for error events with
Say you've got an action creator that loads the user's theme preference, light or dark, but you want to handle the case where storage is unavailable. If the action succeeds, you get a theme. If it fails for any reason, you want to gracefully fall back to some default. You would use
handleAction(...) to successfully set a theme, and
handleAction.error(...) to use the fallback.
All errors are reported. If the action creator fails for any reason, your error reducer will be notified.
If you're using TypeScript, you may notice that error payloads are always marked
unknown. That's because
throw types are inherently unknown - anything can go wrong.
If you're anticipating a specific error, then use (or create) a custom error class.
Custom errors are unusual in web development, but that's unfortunate. You can only win by having more descriptive errors. This works particularly well with TypeScript because after the
instanceof guard, you can safely access any custom fields on the error class. Use this to send arbitrary data to the error handler.
You can handle async actions the same way you would synchronous actions. Reducers for async actions are called when they finish, but occasionally you'll want something more immediate: a loading flag, an optimistic patch, an instant deletion. That's why retreon provides a hook for when an action starts, called
Optimistic reducers use the action input as the payload (that is, whatever you passed to the action when it was called).