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.
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.
Everybody knows this, but let’s repeat it one more time. The Redux app has the next structure:
All this staff responds to the side of logic. Let’s look further. What’s about views?
There are two types of components:
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
After everything we’ve talked about, we can define two groups:
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.
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:
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.
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^^
Check out our newsletter