React + Redux: Architecture Overview

Colt Pini
MoFed
10 min readNov 20, 2016

--

React is pretty hot stuff right now. I have been working with it for a while now. As I was coming up to speed there were a lot of sticky points to it that I didn’t understand well and felt like a wall, A big one.

Early on I was shown what the overall architecture looked like, but didn’t have a code reference as to what they were or an understanding. Now that I have been using it for a bit, I am going to try and fill in the gaps that I found getting started to help anyone who may stumble across this article.

React is…

If you are starting from the beginning, you may have heard this react buzzword and are trying to figure out what that means. The truth is, react doesn’t mean what you think it means, probably. As I have started developing with React, it has been more about the architecture than the actual view. React is a view. There are other parts that wire things up to that view.

The thing that I have liked most about React has been the loosely coupled architecture, the thing that has been the hardest is the loosely coupled architecture.

So let’s define react as an architecture. Loosely defined, you have an API, a Store, and a View. The API is the service that I am hitting for data, to get or set. The Store is where the data is stored in our app. The view is where the store data is combined with markup to output the rendered page.

In the stack that I am using the View is React. The store is Redux. and there is a lot of glue between that to make it work, like react-router, webpack, babel and others, but I am not going to dive into those. I am going to really focus on the view and the store and how they interact. Define the pieces and show how they work together with code.

The overall architecture

I want to introduce you to the architecture and layout how each connection is made when we talk about each part.

The overall architecture for React + Redux

There are a lot of parts there, but each plays a very specific purpose. These purposes will be explained as we get into each part. At this time, it is important to know that Services, Actions & Action Creators, Reducers, and Selectors are redux. Templates and Components are react. The Container is piece that allows them to be loosely coupled together. That is really important because it allows the store and the view to be able to change and grow independently of each other.

I am going to keep building on that diagram.

Adding state.

When I talk about state what I am talking about is all the data that is needed for the app to be in a particular…well state. State being the condition of a person or thing, as with respect to circumstances or attributes. This state is in the store for redux, and in the props for react. When the store is updated it re-maps through the container and re-renders the DOM. When there is an event in the DOM that triggers events through the container and to the Actions. The biggest thing to know here is that the store is where redux stores the data, the data is mapped to props, where React displays through components. And all that is state.

So let’s map the relationships.

Mapping of connections between pieces

Wow. Ok. There is a lot going on there. Hopefully, I can now explain it with some code. I’ll define each piece, with some code, and how it fits in. Let’s start with a component.

As we start getting into code Here I am using ES6 syntax. If you aren’t familiar with that you may want to check out my ES6 series.

Parts and Connections

Component

The component is rendered with a set of props that get passed down from it’s parent component. In this case it is a template, but let’s not fool ourselves, a template, a component, and even a container is just a component. It is a set of code that uses properties and can call other components and gives them properties.

A component really starts with an API. What data do I need in order to render something. For an example let’s think about a tile, a very root level component used in a lot of places and is almost on every website.

So, let’s define a tile. It needs an image, a title, a teaser description, a link, and sometimes it has some metadata.

import {Component as ReactComponent} from 'react';class Component extends ReactComponent {
render() {
const {image, title, teaser, link, meta} = this.props;
return (
<div>
<img src={image} alt=""/>
<span>{title}</span>
<p>{teaser}</p>
<p>{meta}</p>
</div>
)
}
}
export default Component;

The thing to notice most here is the props. Those props get mapped to HTML markup using JSX. This isn’t about creating components so I am not going to get to detailed in this article.

So if I was to use this it would look like this:

<Component title={title} meta={meta} image={image} link={link} teaser={teaser} />// or you could use the object spread operator
<Component {...props} />

So you can see that the component gets used by passing properties down to it. Then the components react to the properties coming down. You do not edit DOM! You edit data that then edits the DOM. That is really huge! The other part of that is that data never goes up, instead an event is triggered and sent through the Container to an action to start the cycle.

Template

I am not going into much detail here as it is simply a component. But I do want to mention it is a special kind of component that does things more specifically than the lower components do. I refer to this as adding opinions to the components. A component may allow any title, but the template may only pass down a single title for anything that is there. That would be an opinion.

Container

The container is the glue that connects react to redux. It connects it in a lot of ways. This is where the react-redux module is used and I usually call that connect as that is what I use it for. It takes three arguments, an object that maps state to props — mapStateToProps, an object that maps actions to dispatch (these are used to wire events to actions) — mapActionsToDispatch, and mergeProperties merges all the properties together and passes them to react for rendering.

Actions & Action Creators

I put these together because they are kinda referred to as each other. Often when people are referring to actions they really mean action creators. What is the difference?

Action: This is an object that contains the type of action and the state that was changed because of the action.

Action Creator: This is the code that is called to create an action and send it along to the reducer.

I kinda think of the Action as an event for redux. When an event is fired there is an event type (like onClick, onHover, etc.) and has an event object that contains the data from the event. Well an action is kinda the same, it has a type and the data.

Let’s look at how Containers and Actions play together. This is the container file:

//Step 1: Pull in the function for the action.
import { getArticles } from '../store/articleList/actions.js';
//Step 2: This connects the action, getArticles, to dispatch the action.
const mapActionsToDispatch = (dispatch) => ({
loadArticles: () => dispatch( getArticles() )
});
//Step 3: This takes the connected action and merges it to a prop for React.
const mergeProps = (state, actions) => ({
...state,
...actions,
loadArticles: () => actions.loadArticles()
});
//Step 4: Merging it to a prop makes it available in the component,
// in this case on the componentDidMount life cycle event.
// This could just as easily be a click, submit, type, or other type of event.
class LandingContainer extends ReactComponent {
componentDidMount() {
const {loadArticles} = this.props;
loadArticles();
}
render() {
return <Landing {...this.props} />
}
}

If you follow the structure you can see how an action get mapped to a property in a component. The file above was rearranged a little so you could see the flow more easily.

Now lets look at the action:

// This is the connection to the Reducer. This is where the action type is connected.
import {ADD_ARTICLE, CLEAR_ARTICLES} from './reducers.js';
// This connects redux to the API.
import getArticleList from './services.js';
/* Sync Action Creators */
//By default all actions are synchronous. But we need to call an outside API.
// Here is where the action is created, a type, with a set of data.
export const addArticle = (article = []) => ({
type: ADD_ARTICLE,
article: article
});
// This is also a creator, a simple one. All it will do is remove what ever state is in the store for articles.
export const clearArticles = () => ({type: CLEAR_ARTICLES});
/* Async Action Creators */
// Here is the async creator. This is using Thunk that allows us to pass a function as the data in an action.
// it gets run on the other side. Here we calling our service, getArticleList, that returns a promise.
// then we dispatch the action when the promise is resolved.
export const getArticles = () => (dispatch) => {
getArticleList()
.then( (articles) => {
articles.forEach((article) => {
dispatch(addArticle(article))
})
})
};

That is how the action is connected to a service, connected to the container, and gets data. But how it sends the data on… for that we need to move to the reducer.

Reducer

The key thing to know about a reducer is that for every dispatch every reducer is called and given the dispatched action. Then it is up to the reducer to handle it or pass it on.

// This is declaring the types of actions. 
export const ADD_ARTICLE = `ADD_ARTICLES`;
export const CLEAR_ARTICLES = `CLEAR_ARTICLES`;
// This will be called for every dispatch. It is passed the type of action.
export default function(state = [], {type, article } ) {
// Here I ignore or handle each action type.
switch(type) {
case CLEAR_ARTICLES:
return [];

case ADD_ARTICLE:
return [
...state,
article
];
// if I don't care about the action I just pass along the state that was given.
default:
return state;
}
}

The other part of the reducer that is really important to know is that you don’t manipulate state. You create new state. So rather than doing something like this state.property = "some new value" instead you would do return [...state, property]. State then goes into your store.

Selector

The selector is how you would get data out of your store in the container. As I show the code it may not seem very valuable.

//Selector.js
export default (state) => (state.property)
//Container.js
import selectArticles from '../store/articleList/selectors.js';
const mapStateToProps = (state) => {
return {
// React is looking for the tiles property in the template, here we map articles to tiles.
tiles: selectArticles(state)
}
}

Here is showing how the selector is defined and used in the container. But why use selectArticles(state) here instead of state.property directly? Because the container shouldn’t care about how the store is organized. I can change and do minor tweaks in the selector before it is sent on to the container. It has a lot of power.

Home Stretch

So we have now covered the round trip. The component used an event, in our case componentDidMount, that was connected through the container to an action. The action got some data from an API and using thunk we were able to do it asynchronously. We learned that the dispatch sends the action to all reducers and they choose to handle them. When a reducer handles an action it doesn’t modify state, it creates a new state and puts it in the store. When the store is changed react reacts and the data is retrieved from the store using a selector in the container. Once retrieved it is mapped to props and sent down the tree to the components and the components are re-rendered based on the updated store. Ahhhhh (Long sigh of relief). done.

Where to go from here?

I have three repos that have demo code in them. There are three pieces: react demo tile, react demo landing, and react demo site. It walks through building a landing template that uses the tile, then wiring up the data in the site. I warn you, it isn’t the prettiest thing in the world, but that isn’t the point of this article. I’ll have others on css details and making it look nice, but later.

Each repo has a set of numerical tags. Each represents a major step, kinda a check to see if the code you built from each step matches the code that I did.

Side note: In the demo_site I use an API that is being hit is an internal API. In the final state of the demo site I am hitting this medium publication as a public API to hit, for earlier section you can replace the internal API with the medium API and it will work.

--

--

Colt Pini
MoFed
Editor for

Disciple, Husband, Father, Fisher, Principle Ux Designer, Lead Developer, Italian American.