Action(s) in both Redux and Flux simply refer to objects that
…represents an intention to change the state.
Actions are the only way to get data into the store.
…represent the intent to perform some manipulation of the data in a Flux application…
The only way to update stores is to send them actions.
An example is given below of how synchronous and asynchronous actions are created in our Flux class.
Our React components contain a reference to a SampleFlux class, and call
flux.actions.asyncAction(...)to trigger calls to update the Flux store (example below in the Components section).
Mapping synchronous Flux actions to Redux actions is pretty straightforward; whereas the function
syncAction in Flux called
this.dispatch with the type of action and the payload data for the store,
syncAction in Redux simply returns an object that contains the type and and payload.
To map asynchronous actions to our Redux actions, we used a helper library called redux-thunk to help map our asynchronous action creators into a version that can be called just like synchronous action creators. We can also reuse our existing service used to make HTTP calls.
These actions are bound to the dispatch function provided by Redux’s
bindActionCreators() function and passed as props to the components via React-Redux’s
connect(). By doing so, the actions can be dispatched from the React components via simply calling
this.props.asyncAction(…) to trigger an update in the Redux store (example below in the Components section).
Reducers are pure functions from the functional programming world.
A reducer (also called a reducing function) is a function that accepts an accumulation and a value and returns a new accumulation. They are used to reduce a collection of values down to a single value.
In Redux, the accumulated value is the state object, and the values being accumulated are actions. Reducers calculate a new state given the previous state and an action…
Reducers are the most important concept in Redux.
Converting from mutating the data directly in our Flux stores to Redux reducers and maintaining immutability was definitely much more complicated than converting the actions. However, lots of the logic in our Flux stores could be reused in our reducers, and thinking about the state update in terms of pure functions immediately made it easier to reason around — we knew exactly what went in and out of the reducers because they’re free of side-effects!
In our Flux stores, we initialize our state and bind each action type that we listen for to an associated handler function. In our handler functions, we mutate the state, and emit a change event that updates our components with new state.
Now instead in Redux, when the reducer listens for and receives an action with some action type, it will perform a switch statement based on that action type, and perform some logic in the right case statement. Depending on whether or not the state changed, the reducer either returns a newly created copy of the state with the updated values, or the existing state to indicate no change.
Depending on how complicated the store was, we could break down our mapped reducer via reducer composition to maintain readability and clarity of logic, or completely separate out the state into two or more reducers, and use Redux’s
combineReducers to combine our smaller reducers into a single larger reducer. This is how we approached changing our Flux stores to reducers.
Notable differences between the Flux store and the Redux reducer include:
- Taking into consideration how to treat the state as immutable under the Redux architecture compared to directly mutating the state under the Flux architecture. This means always returning a new state if the state has been updated via
Object.assign()or the spread operator
- The boilerplate of explicitly emitting a change event in Flux is now gone
this.emit('change')! Redux handles publishing for us, so the code is shorter and less repetitive.
At this point, we also added selectors to return specific parts of our state and perform calculations on the state that could be memoized in order to boost performance.
Last but not least, we had to refactor our containers and components to receive state from Redux instead of Flux. Whereas before our components would receive state directly from the Flux stores via mixins and use
this.state everywhere, our components now would receive the Redux state via props passed by Redux’s
connect() and use
Hence the following component tied to Flux —
becomes the following component connected to Redux —
And we’re done! The first pass of mapping Flux to Redux ends after we’ve updated our components, and they’ve successfully rendered and passed our tests (you should have plenty of tests!).