React Components Best Practices
When developing an application using a particular library/frameword, you need to know about it's best practices and style to create a clean and concise code.
These are based on my experience working in a team.
One Component per file
In react simplest way to define a component is to write a JavaScript function.
//This is called functional component because it's literally a function
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
Other way is to extend component class from React.Component which provides us with react lifecycle method.
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
There should be only one component per file. We should also avoid React.createElement unless using a file that is not JSX. I would suggest to always use JSX.
Stateless vs Statefull
Always use stateless function unless you need to use react's life cycle methods, refs or state.
//Stateless component. No need for state, refs or life cycle method
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
//Statefull component.
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {'Tehmina'};
}
componentDidMount() {
this.timer = setTimeout(
() => this.tick(),
1000
);
}
componentWillUnMount() {
clearTimeout(this.timer); //We should always destroy these kind of handlers
}
tick() {
this.setState({name: 'Faizan'});
}
render() {
return <h1>Hello, {this.state.name}</h1>;
}
}
Refs
Use ref callback.It's much more declarative
//Bad
<Welcome ref="myRef" />
//Recommended
<Welcome ref={(ref) => { this.myRef = ref; }}
/>
JSX tags in render method
When returning from render method and returning JSX tags, if they are more than one line wrap them in parentheses. You can also assign child tags and then use only one line.
//Bad
render() {
return <Welcome className="class">
<child />
</Welcome>;
}
//Recommended
render() {
return (
<Welcome className="class">
<child />
</Welcome>
);
}
//Or this one also good
render() {
const child = <child />;
return <Welcome className="class">{child}</Welcome>;
}
Event Handler
Always bind event handler used in render inside constructor or use arrow function because it automatically binds to this context outside function it's being used.
class Hello extends React.Component {
constructor(props) {
super(props);
this.onClickDiv = this.onClickDiv.bind(this);
}
onClickDiv(event) {
}
render() {
return (
<div onClick={this.onClickDiv} /> //recommended
<div onClick={ (e) => this.onClickDiv(e) } /> // Not a good practice because when re-render componnent it would create new closure every time
);
}
}
DefaultProps vs PropTypes
Always use default props for non-required props field. It makes sure your component is doing what you expect it to do and eliminates certain type checks and inform other developers about their usage. PropTypes let developer know what props are being used for current component.
function Welcome({name, height, children}) {
return <h1>{name}{height}{children}</h1>;
}
Welcome.propTypes = {
name: PropTypes.string.isRequired,
height: PropTypes.number,
children: PropTypes.node
}
Welcome.defaultProps = {
height: 0,
children: null //Null is of type object in javascript with no value
}
Higher-Order Components (HOC) vs Mixins
An higher-order component in react is a pattern for reusing component logic. HOC is a function which takes component as argument and returns a new component for reuse. In react usage of mixins is discouraged, this is because they introduce name clashes, implicit dependencies and harder to track once developed and used because a mixin can use another mixin.
For example mixin usage when using context in react.
var RouterMixin = {
contextTypes: {
router: React.PropTypes.object.isRequired
},
// The mixin provides a method so that components
// don't have to use the context API directly.
push: function(path) {
this.context.router.push(path)
}
};
var Link = React.createClass({
mixins: [RouterMixin],
handleClick: function(e) {
e.stopPropagation();
// This method is defined in RouterMixin.
this.push(this.props.to);
},
render: function() {
return (
<a onClick={this.handleClick}>
{this.props.children}
</a>
);
}
});
module.exports = Link;
Because use of context is broken in react, culprit is shouldComponentUpdate. You want to hide it's implementation and keep it in mixin.
Solution is use of HOC. For other alternative solutions to mixin are composition of components and utility modules.
function withRouter(WrappedComponent) {
return React.createClass({
contextTypes: {
router: React.PropTypes.object.isRequired
},
render: function() {
// The wrapper component reads something from the context
// and passes it down as a prop to the wrapped component.
var router = this.context.router;
return <WrappedComponent {...this.props} router={router} />;
}
});
};
var Link = React.createClass({
handleClick: function(e) {
e.stopPropagation();
// The wrapped component uses props instead of context.
this.props.router.push(this.props.to);
},
render: function() {
return (
<a onClick={this.handleClick}>
{this.props.children}
</a>
);
}
});
// Don't forget to wrap the component!
module.exports = withRouter(Link);
Lists and Keys
When we add a key to element inside array, it improves our redering perfromace. Key tells react which element is chaged, added or removed. Key should always be unique and not index of array.
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
shouldComponentUpdate for increase performance
This lifecycle method is run before re-rendering process. When a component's props or state change, React compares new values with rendered one. When they are not equal, React will update the DOM.
By default it returns true, leaving React to perform the update. You can use shouldComponentUpdate(nextProps, nextState) and return false by comparing nextProps to rendered (previous) value for halting all the re-rendering of child branches in a component. It can improve performance significantly when you don't need to re-render a component.
class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) { //By using immutable library we can avoid deep comparison of objects as well
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}
We can also use React.PureComponent for this but remember this only uses shallow comparison.
I hope you found this article useful. I would be touching on redux next.
<div onClick={ (e) => this.onClickDiv(e) } /> // Not a good practice because when re-render componnent it would create new closure every time
I think React solved it so in performance it’s the same, just a question for readability
great work bro …
Thanks for that article :)
You have some typos in https://www.codementor.io/faizanhaider/react-components-best-practices-9xcbq5uwe#lists-and-keys, you missed some “n” at the very begining.