Redux introduction with NgRx (part 3): Redux core concepts

Benjamin Cabanes
5 min readJul 8, 2018

--

Redux introduction with NgRx

This article is part of the serie of articles that you may want to read before starting this one.

Here is the summary:

  1. Store & Application State
  2. Redux Principles
  3. Redux core concepts (you’re here)
  4. NgRx high level introduction
  5. NgRx syntax explained

Redux core concepts

Redux has some core concepts that are the basis of its existence.

Redux core concepts

Single state tree

The whole Application State is constituted of a tree representing your data. In this tree, we can find multiple branches for each part of our application. The tree’s data structure is the gathering of all the branches’ data structure together.

Plain Javascript Object. The single state tree is just one plain old Javascript Object. Nothing is really special here. We can have any type of data in this object as soon as it can be serializable.

Composed by reducers, because a tree has multiple branches that live together in a coherent organism, your state tree is created by multiple branch of data structures.
Everything is kept as one big object and it is composed by reducers. The reducers are the functions that will do the actions on our data structure and will return our new data structure.

Let’s be original… and take a Todo application for the example:

const state = {
users: [{id: 1, name: ‘Sam’}],
todos: [{id: 1, label: ‘Go see a movie’, done: false}],
isLoading: false
};

As you can see in this simple example, the whole concept of Store is in reality implemented by a single plain old JavaScript object. All the data needed by the application and its data structure is represented by this Store implementation.

Actions

Actions are payloads of information that send data from your application to your store. They are the only source of information for the store. Actions are again, plain JavaScript objects.

  • Action has two properties

type| string | required: Represent the id of the action emitted;
payload | any | optional: Represent the data needed for performing the state change;

  • Dispatch actions to reducers

This action is handled by the Store and passed down to the reducers. The reducer is a pure function that accepts the state and the action.

If we would add a new todo into the Store with our Todo application, we will need to create and then dispatch an action that represent the change we want to perform.

const action = {
type: 'ADD_TODO',
payload: {
label: 'Go to the grocery',
done: false
}
};

Reducers

Actions only describe what happened, but don’t describe how the application’s state changes. This is the role of reducers. They specify the manipulations to do on the application’s state for each action sent to the Store.

Reducers are written with pure functions. It allows use to test it easier, and to only have one representation of state at a particular point in time.

Reducers receive dispatched action. Reducer responds to action.type, and depending on its type, the manipulation to get the new state will not be the same (each action is unique). It accesses to action.payload, and grabs the data to make the manipulation. Then it composes the new State and returns it.

For each action, the Store invokes the reducer and gives him the current state and the action freshly dispatched.

function reducer(state, action) {
switch(action.type) {
case 'ADD_TODO': {
const todo = action.payload;
const todos = [...state.todos, todo];
return { ...state, todos }
}
default:
return state;
}
};

The return object is the representation of the new state. The new state is then returned to the Store and bounded to our single source of truth which is our big JavaScript object.

If no actions is matched in our switch statement, we need to return the state too. This returned state will be the same as the previous one, because no manipulation has been done. This is important for the initial state of the application, to not error the reducers and not impacting the integrity of the Store. To stay predictable, Store’s reducers need to always return a state.

A reducer always return a state, even if the action emitted wasn’t on its switch condition.

When the action has been reduced, and the reducer returned the new state, we will have something like that:

const state = {
users: [{id: 1, name: 'Sam'}],
todos: [
{id: 1, label: 'Go see a movie', done: false},
{id: 2, label: 'Go to the grocery', done: false},
],
isLoading: false
};

To sum up, this is how chronologically things are performing together (See schema under):

  1. We want to update the state;
  2. We dispatch an action;
  3. Our action get reduced (manipulations, creation of the new state);
  4. The reducer return the new representation of the state.
Chronological flow

Store

What is the store? The Store is the object that brings the Actions and the Reducers together in a cohesive manner.

  • It is a State container;
  • Components interact with the Store by subscribing to/selecting slices of State & by dispatching Actions;
  • Store invokes Reducers with current State and dispatched Actions with a type and a payload;
  • Reducers compose new State in an immutable fashion;
  • Store is updated, notifying subscribed components.

Unidirectional data flow

Redux architecture revolves around a strict unidirectional data flow. Meaning that there is always one way for the data to flow. Here a schema of the representation.

Redux Unidirectional data flow
  1. We have a component that DISPATCH an Action;
  2. The Store handle this Action and sends it to the Reducer;
  3. The Reducer function (which is pure) is executed against the state of the application along with the type and payload of the Action. We can say that Our action gets reduced (manipulation, creation of the new state);
  4. The reducer return the new representation of the state;
  5. The component is selecting the part (or slice) of the Application State that it needs to render the data.

Redux can be seen as a pattern to handle the state changes of the application. It makes it simple to reason about the Application State. Attention, simple doesn’t mean Easier!

With all these explanations on the Redux architecture and Pattern, let’s see some NgRx! See you in “NgRx high level introduction”.

--

--

Benjamin Cabanes

Javascript Architect @nrwl | @NXdevtools core team | 🇨🇦