Using React (-Native) with Redux and Redux-Saga. A new proposal?


Data Flow with Redux and Redux-Saga (proposal)

I really love the principles I learned after switching from AngularJs to React and its Flux architecture. At the moment I’m working on a real world application for another company. After my first React-Native App with Redux, I decided to add Redux-Saga to the mix. Complex transactions and side effects should be readable and easy to maintain too.

This blog entry focuses on how I use Redux with Redux-saga together. It assumes you have basic knowledge of React, Redux, Redux-Saga and ES6/7.

My main proposal is to split your Actions into two different categories:

  1. User- & System-Actions
  2. Reducer-Actions

User- & System-Actions

These types of Actions will get dispatched by React Containers, depending on some User input or by some App lifecycle methods (e.g. componentDidMount). These types of Actions will never get handled by the Reducer. Instead they will only work as input for the Saga.

Reducer-Actions

Reducer-Actions are dispatched by Saga’s. Only they will change the state of the App. In addition, every Reducer-Action has exactly one responsibility and will get processed by exactly one Reducer.

What are the consequences?

With this Action-Type structure you will get a very clean data flow for free. Before I started following these principles I was never sure if I should dispatch two separate Actions (e.g. fetchData & setLoadingSpinner) or if I dispatch only one Action and handle it in two Reducer. Both have drawbacks. In the first case, you have to dispatch two actions with logic who could change over time. In the second case you have no easy way to tell what will change in your AppState. With some side effects mixed in, you have no clue at all what will happen with your App.

With this new proposal you should dispatch one expressive User-Action in your Container (e.g. selectProduct) and zero, one or more expressive Reducer-Actions in your Saga (e.g. setSelectedProduct, setLoadingSpinner, someOtherStuff).

Because of the Sagas generator function style, you can read it line by line, even with complicated side effects. Also you can easily include some higher order functionality like “takeLatest” or “takeEvery” for your User-Actions.

Example Saga: take one User_Action (every or latest) and map it to one or more single responsibility Reducer_Actions.

To make this work and keep it clean, you will need to create one Saga for every User-Action. With this setup every new developer on your project can immediately see what is possible and what will happen in your App. You only have to show him or her all of the User-Action-Sagas. Nothing else can happen.

more details and examples coming soon…

To reduce the boilerplate overhead introduced with redux-saga, we can omit the standard ActionCreators.

Things we could do in the future:

  • The Reducer should ignore all User-/System-Actions by default
  • Generate a flowchart, which displays the connections between User-/System-Actions and Reducer-Actions