React-Redux

I’ve been trying to get a clear picture of the data flows in React-Redux, and I thought I’d share that mental flowering. I’m still quite new with React-Redux so I’d suggest reading it with a note of caution but knowing that the end result was working software.

Redux

In Redux itself the data flow seems relatively straight-forward:
1. When something happens we dispatch an action to the store i.e. store.dispatch({ type: "TYPE_KEY", ... }).
2. Redux then calls the reducer which returns a new state back to Redux.
3. Functions that were previously registered using store.subscribe(fn) are called and those functions call store.getState() to get the new state.
This seems like a fairly classic observer pattern around an event-store.

If I were translating this to React then I’d expect that within my component I’d register a listener against the store that would call this.setState({ ... }). Then setState() would trigger the framework to re-render that component. The problem with this plan and react-redux is subscribe() is nowhere to be seen. Nor for that matter is store – so where did they go?

The answer is in the connect() method.

Connect()

When connect() is called on a component used inside a <Provider> tag, connect() automatically picks up the Provider’s store. This store is configured at the highest level of the application, something like this:

import { createStore } from "redux";
import { Provider } from "react-redux";
import reducer from "./state/reducers";

let store = createStore(reducer); // reducer is the outcome of combineReducers()

ReactDOM.render(
    <Provider store={store}>
        <Router />
    </Provider>,
    document.getElementById("example")
);

Because connect() encapsulates the store, it needs to provide an interface to allow the component to (implicitly) access the store. It does this via two arguments, which the documentation calls mapStateToProps and mapDispatchToProps.

mapStateToProps

mapStateToProps is effectively a filter that selects the bits of the global state from the store that are of interest to the component. The bits of state returned from this method are added to the this.props object.

This was the slightly counter-intuitive part in migrating from storing the data in React state. React documentation indicates that the data owned by a component should be stored in this.state. When it’s transferred to the store, that component is no longer strictly the owner and shouldn’t be changing it without informing the store, so it makes sense for it to be read-only, and therefore it moves to this.props.

mapStateToProps is the replacement for store.subscribe(). With it in place, any updates made to the store are filtered by the object returned from this method, and are then applied to the component’s this.props, causing the standard component updating lifecycle including the render.

mapDispatchToProps

mapDispatchToProps allows us to access the store.dispatch(). It provides the dispatch function as an argument, and should return an object containing methods that call dispatch with various actions. e.g.

const mapActionToProps = (dispatch) => {
    return {
        requestUsers: () => dispatch({ type: "REQUEST_USERS" }),
        updateUser: (user) => dispatch({ type: "UPDATE_USER", user: user })
    }
}

This enables the component to trigger actions by calling this.props.methodname(args).

connect(mapStateToProps, mapDispatchToProps) returns a function which should be passed the class or function of the component using the store. Convention also has that class returned as the default export so it can be directly imported. e.g.

// in component file
export default connect(mapStateToProps, mapActionToProps)(ComponentName)

// in file that is using the component
import ComponentName from "./ComponentName"

React-Redux Cycle

Putting it altogether we get a data flow something like this:
1. When something happens we dispatch an action to the store by calling an action property. i.e. this.props.requestUsers().
2. This calls dispatch(action-object) inside the method used in step 1, whose definition is found in mapActionToProps.
3. Redux then calls the reducer which returns a new state back to Redux.
4. The component receives the new state, filters it according to mapStateToProps and applies the filtered object to this.props triggering the normal React component update lifecycle and render.