React Components Best Practices

Published Jul 12, 2017Last updated Nov 25, 2017
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.
should-component-update.png

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.

Discover and read more posts from Faizan Haider
get started
Enjoy this post?

Leave a like and comment for Faizan

17
6