Redux Structure: a Way To Success

Lift off

Today, I want to tell you how my point of view on components and app structure has changed since I’ve been writing on React with Redux.

What is all this bustle about?

There is so much hype about React and especially Redux, which has won the fight in flux stack already behind the scenes. Recently, Redux came into my life too, and, honestly, I like it very much. Of course, as any other tool, it has some advantages and disadvantages. It turns out that it’s just a tiny library with good data-flow, and we don’t have any framework over it, which tells us how to implement features — only docs, articles, and our decisions. So we should do all the project structure on our own.

Redux structure. Let’s figure it out

Everybody knows this, but let’s repeat it one more time. The Redux app has the next structure:

  • Store — an object that keeps whole the state of our application
  • Actions — plain objects representing the facts about “what happened” in our app
  • Reducers — pure functions updating the state according to actions

All this staff responds to the side of logic. Let’s look further. What’s about views?

There are two types of components:

  • Container — “smart” components, which are concerned with “how things work”
  • Presentational — “dumb” components, which are concerned with “how things look”

The main idea here is that only a container component could manipulate the state. The presentation component just represents content according to props, which are provided by the closest container and sometimes trigger a container’s callbacks.

Container component example:

import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { reduxForm } from 'redux-form'

import * as postsListActions from './actions'
import PostsList from 'components/PostsList'

const mapStateToProps = (state) => {
  return {
    posts: state.postsListStore.posts
  }
}

const mapDispatchToProps = (dispatch) => {
  const actions = bindActionCreators(postsListActions, dispatch);

  return {
    addPost: (postParams) => {
      actions.addPost(postParams)
    },
  }
}

const PostsListContainer = reduxForm({
  form: 'AddPostForm',
  fields: ['title', 'content']
}, mapStateToProps, mapDispatchToProps)(PostsList)

export default PostsListContainer

Presentational component example:

import React from 'react'

import Item from './children/Item'
import Form from './children/Form'

import './assets/style.scss'

class PostsList extends React.Component {

  postBuilder(post, n) {
    return (
      <div className='post-list__item' key={n}>
        <Item title={post.title} content={post.content}/>
      </div>
    )
  }

  render() {
    return (
      <div className='posts-list'>
        <div className='posts-list__form'>
          <Form {...this.props} />
        </div>
        <div className='posts-list__content'>
          { this.props.posts.map(this.postBuilder) }
        </div>
      </div>
    )
  }
}

export default PostsList

Let’s do a little combination

After everything we’ve talked about, we can define two groups:

  • (presentational components and their assets) — these can be displayed according to received props and can trigger callbacks, which also come from props.
  • (actions, reducers, containers) — these can manage the application store and work with API, know which props should be passed to inner presentational components.

So according to this, we divide this into two groups and get a structure like this:

components/
 - PostsList/
modules/
 - PostsList/
 - reducers.js
root/
store/
utils/
index.js

Let’s call the first group “components” and the second “modules“. We still have a file called “reducers” where we combine reducers from each module.

Modules vs. Components

The main goal here is to understand when you should create a module instead of using another component passing props and callbacks. If you pass some props through a component just to get these props inside the inner component or use a component in a module which is not fully responsible for this component behavior. This might be a good time to think about creating a new module.

Example of a module with its own actions, constants, and reducers:

modules/
  PostsList/
   - actions.js
   - constans.js
   - reducers.js
   - index.js

Example of a component with its own assets and inner components:

components/
 PostsList/
   assets/
    - icon-plus.svg
    - style.scss
   Form/
   Item/
    - assets/
    - index.jsx
   index.jsx

If you know how to divide “smart” and “dumb” components in Redux, you can divide modules too. The idea is the same. But let’s look at another example that might not be so simple. First of all, remember that each module should be responsible for a specific part. In the example above, we had one top-level presentational component with two children. They don’t know about the store and their parent component tells them everything they know about our application. But what would be changed if we had the ability to control, for example, post-publication?

This picture illustrates what I mean:

change publication status

Now we have a component with its own logic because each item in a list could change its publication status. It isn’t a list’s problem at all. And it seems we should create a new module. Its structure can be a little confused because we only have one index.jsx file and use other module actions. Using other module actions gives us the ability to change them. That is how our modules “communicate” with each other — one module just changes part of a store when another is listening.

Example of a module which uses another module’s actions:

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

import * as postsListActions from 'modules/PostsList/actions'
import PostsListItem from 'components/PostsListItem'

const mapDispatchToProps = (dispatch) => {
  const actions = bindActionCreators(postsListActions, dispatch);

  return {
    togglePostPublication: (id) => {
      actions.togglePostPublication(id)
    },
  }
}

const PostsListItemContainer = connect(null, mapDispatchToProps)(PostsListItem)

export default PostsListItemContainer

The main advice here is that you shouldn’t be afraid of creating one more module. It doesn’t worsen performance and doesn’t make the code complex.

What’s in the end?

We received a collection of presentational components with their own styles and even images inside them, which are easy to test and reuse. We can even create a single collection of such components for multiple projects and plug in only the necessary components from project to project. Also, we get a collection of logical cells — modules. And it looks great to me because we can reuse them too. They are fully independent parts of logic. And the last benefit I see is that we can develop them separately from a project where they will be used. In this case, it’s much easier to concentrate on the result to find a better solution.

Well, we’ve got a cool UI constructor on our hands, thanks to Redux^^

Kirill Mitskevich

Kirill Mitskevich

web developer at datarockets

From our blog

Stay up to date

Check out our newsletter