Presentational and Container Components for React with Redux
In this post I’ll briefly describe small refactoring of React component based on this article
I’m creating an SPA using React and Redux library for Flux architecture.
MyComponent - old way
MyComponent.js
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { myAction } from '../actions' // actions
import styles from './styles.scss' // CSS Modules
class MyComponent extends Component {
render() {
return (<div className={styles.myClass}>MyComponent...</div>)
}
}
MyComponent.propTypes = {
propFromUrlParam: PropTypes.string.isRequired,
propFromState: PropTypes.string.isRequired,
myAction: PropTypes.func.isRequired
}
function mapStateToProps(state, props) {
let { propFromUrlParam } = props.params; // props from URL params
let { propFromState } = state; // props from state
return {propFromUrlParam, propFromState}
}
export default connect(mapStateToProps, {
// mapping methods to component
myAction
})(MyComponent)
This file is going to be really big and one of best practices is to divide it into two logical parts: Dumb and Smart or Container and Presentational components. All the explanation you can find here. I’ll just show how it looks in my case after dividing. So,
MyComponent - new way
MyComponent.js
the presentational component:
import React, { Component, PropTypes } from 'react'
import styles from './styles.scss' // CSS Modules
class MyComponent extends Component {
render() {
return (<div className={styles.myClass}>MyComponent...</div>)
}
}
MyComponent.propTypes = {
propFromUrlParam: PropTypes.string.isRequired,
propFromState: PropTypes.string.isRequired,
myAction: PropTypes.func.isRequired
}
export default MyComponent
MyComponentContainer.js
the container component:
import MyComponent from './MyComponent' // presentational component
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { myAction } from '../actions' // actions
function mapStateToProps(state, props) {
let { propFromUrlParam } = props.params; // prop from URL params
let { propFromState } = state;
return {propFromUrlParam, propFromState}
}
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators({
// mapping methods to component
myAction
}, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
And a small helper index.js
which is used just for convenience while importing the component and looks like this:
export default from './MyComponent' // container components
Now you may import the MyComponent
wherever you need it
import MyComponent from './components/MyComponent' // will import index.js