Building Modular Redux Applications

or: how to stay sane when developing medium to large applications and not be terrified of having to implement new features

Gian Marco Toso

@gianmarcotoso

gianmarcotoso

gianmarcotoso.com

polarityb.it

Software Engineer, Nice Guy

The motivation for this talk

Code Reusability

Better work distribution among teammates

Separation of Concern

So, what are we going to talk about?

Presentation Layer

Data Layer

Modules

Application Structure

What is an application made of?

Store

Presentation

Action

Reducer

Presentation Layer

Present

How does it work?

As the name suggests, this layers should provide some sort of visual representation of the data it's consuming

Interact

Most applications require interaction from the user. The presentation layer should expose some sort of interaction mechanism allowing the user to perform actions on what's presented

Data Layer

Fetch Data

What does it do?

From a server, a local data store or any other source, either synchronously or asynchronously, and store it in its own state

Provide Data

Have a publicly consumable surface which allows an external entity to consume the data in the store, either raw or reformatted for a specific purpose

Manipulate Data

Have a publicly consumable surface which allows an external entity to manipulate the data in the store, and save it either locally or on an external store such as a server or a local database

ReactJS

  • Handles Presentation
  • Allows to create components
  • It's awesome to work with

ReactJS

  • Handles Presentation
  • Allows to create components
  • It's awesome to work with

Redux

  • Handles Business Logic
  • Implements the Flux pattern
  • It's awesome to work with

+

+ a 1000 other modules...

=

An application stack!

ReactJS and Redux work together by connecting components

import React from 'react'
import { connect } from 'react-redux'

class MyComponent extends React.Component {
    /* ... */
}

const select = state => { /* ... selector code ... */ }

export default connect(select)(MyComponent)

(thanks to the `react-redux` library)

Modules

Notable Mentions

Ducks

"A module...

  1. MUST export default a function called reducer()
  2. MUST export its action creators as functions
  3. MUST have action types in the form npm-module-or-app/reducer/ACTION_TYPE
  4. MAY export its action types as UPPER_SNAKE_CASE, if an external reducer needs to listen for them, or if it is a published reusable library"

Rules For Structuring (Redux) Applications

  • Modules represent a feature
  • There are strict module boundaries
  • There are no circular dependencies
module/
  components/
  actions.js
  actionTypes.js
  constants.js
  index.js
  model.js
  reducer.js
  selectors.js
// module/index.js
import * as actions from './actions';
import * as components from './components';
import * as constants from './constants';
import reducer from './reducer';
import * as selectors from './selectors';

export default { 
    actions, 
    components, 
    constants, 
    reducer, 
    selectors 
};

What is a module?

A module is a independent unit of an application

A module can define a presentation layer

A module can define a data layer

A module can depend on other modules

A module should depend on other modules as little as possible

Dependency Graph

Presentation Module

Presentation Module

Presentation Module

Presentation Module

Data Provider

Data Provider

Data Provider

Data Source

Data Source

Data Source

Data Source

Separation of concern

Presentation Module

A presentation module is used to define a feature of the presentation layer and can contain:

Components

Routes

Module/
    - Components/
        - MyComponent/
            - MyComponent.jsx
            - MyComponent.spec.jsx
            - MyComponent.css
            - index.js
    - Routes.jsx
    - index.js    /* exports public API! */

A Reducer, Actions and Action Creators *

* If they need to have something on the application state that is not part of the data layer, such as the state of a form, a filter or something else related to the UI

Separation of concern

Data Source

A data source is used to define a feature of the data layer and can contain:

A Reducer

Actions

DataSource/
    - ActionCreators.js
    - Actions.js
    - DefaultState.js
    - Reducer.js
    - index.js    /* exports public API! */

Action Creators

A Default State

Data Provider

A Data Provider is an Higher Order Component that allows for a Presentation Module to consume a Data Source. As its name states, a Data Provider provides data. It doesn't even have to provide data from a single data source, it can provide it from multiple ones!

import { createStructuredSelector } from 'reselect'
import { connect } from 'react-redux'

import * as ActionCreators from 'modules/sources/Posts/ActionCreators'
// Suppose there is an addPost() action creator

let postsSelector = state => state.posts.all
let select = createStructuredSelector({
	posts: postsSelector
})

export default connect(select, ActionCreators)

Connecting Components

import React from 'react'

import WithPosts from 'providers/Posts'

class PostList extends React.Component {
    handleAddPost() {
        this.props.addPost({
            title: 'New Post',
            date: new Date()
        })
    }

    render() {
        return (
            <div>
                <ul>
                    {this.props.posts.map(post => (
                        <li>{post.title}</li>
                    ))}
                </ul>
                <button 
                    onClick={this.handleAddPost.bind(this)}
                >Do Stuff</button>
            </div>
        )
    }
}

export default WithPosts(PostList)

Dependency Graph

Presentation Module

Presentation Module

Presentation Module

Presentation Module

Data Provider

Data Provider

Data Provider

Data Source

Data Source

Data Source

Data Source

Application Structure

Structuring your application well goes a long way when you need to know where to look for a specific piece, or when you need to add or even remove something.

 

An application is not made only by modules, it also has other things like services, utilities, styles, dumb components, configuration files, you name it!

Application Structure

Tips and tricks

Have a folder for each different kind of "element"

- src
    - bootstrap/
    - components/
    - config/
    - core/
    - data/
    - modules/
    - providers/
    - services/
    - utils/
    index.js

Application Structure

Tips and tricks

Place each component in its own folder, with an index.js with a default export to make importing easy!

- AwesomeComponent
    - AwesomeComponent.jsx
    - AwesomeComponent.spec.jsx
    - AwesomeComponent.css
    - index.js

...and don't forget to include tests!

Application Structure

Tips and tricks

You can find a boilerplate application that follows the guidelines of this presentation at

Thank you!

Questions?

I hope you enjoyed this!

Building Modular Redux Applications

By Gian Marco Toso

Building Modular Redux Applications

Slides for my talk at the 2016 ReactJS Day in Verona

  • 2,144