Reducing React / Redux boilerplate with ES.Next
I’ve been working with React and Redux for a while now. To start with I was mostly working from the standard React/Redux example, which meant I ended up with rather a lot of components that look like this:
class SomeComponent extends React.Component {
render() {
return (
<div>...</div>
);
}
}
SomeComponent.propTypes = {
children: React.PropTypes.node,
items: React.PropTypes.object
};
export default connect(
(state, props) => ({
items: state.items
}),
(dispatch) => ({
fetchSomething: (title) => dispatch(Actions.fetchSomething())
})
)(SomeComponent);
It works, but I have a pathological dislike of long boilerplate code, and defining the parameters of a component at the end of the file just doesn’t feel right. Ending up with twenty components all named “StartPage” didn’t exactly help with debugging either. Fortunately, it can be significantly improved by enabling some not yet standard javascript features.
Static properties
Adding the transform-class-properties
babel plugin allows defining properties as well as methods on a class - perfect for propTypes, which I can move to a more visible position, and I no longer have to remember to update the class name in two places when I use the component as a template.
class SomeComponent extends React.Component {
static propTypes = {
children: React.PropTypes.node,
items: React.PropTypes.object
};
render() {
return (
<div>...</div>
);
}
}
Decorators
Decorators are enabled with the transform-decorators-legacy
plugin. The syntax below is equivalent to later calling connect()(SomeComponent)
, but the decorated class can now be exported directly, and the class name is only needed in one place.
@connect(mapStateToProps, mapDispatchToProps)
export default class SomeComponent extends React.Component {
static propTypes = {
children: React.PropTypes.node,
items: React.PropTypes.object
};
render() {
return (
<div>...</div>
);
}
}
Much cleaner, though that does leave out the property mappings. To further simplify things, I ended up making a custom decorator that checks for a static method on the class before calling connect.
@page
export default class SomeComponent extends React.Component {
static propTypes = {
children: React.PropTypes.node,
items: React.PropTypes.object
};
static mapStateToProps(state, props) {
return {
items: state.items
};
}
static mapDispatchToProps(dispatch) => {
return {
fetchSomething: (title) => dispatch(Actions.fetchSomething())
};
}
render() {
return (
<div>...</div>
);
}
}
Ok, so technically this is the same length as the original, but it works much better when the code gets longer than a page, and no repeated renaming is needed when copying it as a new component. There’s also a lot of other common functionality that can be hidden in the decorator beyond the scope of this example - I’m not sure if a universal component extension makes sense yet, but certainly at the app level there is always a bunch of stuff that needs to be applied on every page.