Subscribers are intended to respond to the state value itself, not the action. Updates to the state are processed synchronously, but notifications to subscribers can be batched or debounced, meaning that subscribers are not always notified with every action. This is a common performance optimization to avoid repeated re-rendering.
Batching or debouncing is possible by using enhancers to override
store.dispatch to change the way that subscribers are notified. Also, there are libraries that change Redux to process actions in batches to optimize performance and avoid repeated re-rendering:
redux-batch allows passing an array of actions to
store.dispatch() with only one notification,
redux-batched-subscribe allows batching of subscribe notifications that occur as a result of dispatches.
The intended guarantee is that Redux eventually calls all subscribers with the most recent state available, but not that it always calls each subscriber for each action. The store state is available in the subscriber simply by calling
store.getState(). The action cannot be made available in the subscribers without breaking the way that actions might be batched.
A potential use-case for using the action inside a subscriber -- which is an unsupported feature -- is to ensure that a component only re-renders after certain kinds of actions. Instead, re-rendering should be controlled through: 1. the shouldComponentUpdate lifecycle method 2. the virtual DOM equality check (vDOMEq) 3. React.PureComponent 4. Using React-Redux: use mapStateToProps to subscribe components to only the parts of the store that they need.
The pattern of using functions, called action creators, to return action objects may seem counterintuitive to programmers with a lot of Object Oriented Programming experience, who would see this is a strong use-case for Classes and instances. Class instances for action objects and reducers are not supported because class instances make serialization and deserialization tricky. Deserialization methods like
As described in the Store FAQ, if you are okay with things like persistence and time-travel debugging not working as intended, you are welcome to put non-serializable items into your Redux store.
Serialization enables the browser to store all actions that have been dispatched, as well as the previous store states, with much less memory. Rewinding and 'hot reloading' the store is central to the Redux developer experience and the function of Redux DevTools. This also enables deserialized actions to be stored on the server and re-serialized in the browser in the case of server-side rendering with Redux.
The curried function signature of declaring middleware is deemed unnecessary by some, because both store and next are available when the applyMiddleware function is executed. This issue has been determined to not be worth introducing breaking changes.
applyMiddleware takes the existing dispatch from the store and closes over it to create the initial chain of middlewares that have been invoked with an object that exposes the getState and dispatch functions, which enables middlewares that rely on dispatch during initialization to run.
combineReducers is opinionated to encourage splitting reducer logic by domain. As stated in Beyond
It's not immediately obvious what a potential third argument to each reducer should be: the entire state tree, some callback function, some other part of the state tree, etc. If
combineReducers doesn't fit your use case, consider using libraries like combineSectionReducers or reduceReducers for other options with deeply nested reducers and reducers that require access to the global state.
If none of the published utilities solve your use case, you can always write a function yourself that does just exactly what you need.
There have been requests to use either the entire
state or the return value of
mapState inside of
mapDispatch, so that when functions are declared inside of
mapDispatch, they can close over the latest returned values from the store.
This approach is not supported in
mapDispatch because it would mean also calling
mapDispatch every time the store is updated. This would cause the re-creation of functions with every state update, thus adding a lot of performance overhead.
The preferred way to handle this use-case--needing to alter props based on the current state and mapDispatchToProps functions--is to work from mergeProps, the third argument to the connect function. If specified, it is passed the result of
mapDispatchToProps(), and the container component's props. The plain object returned from
mergeProps will be passed as props to the wrapped component.