Best Practices for Writing React Components

3 months 2 weeks ago

I remember when I started writing React components; I saw so many different approaches to write React Components in lots of trainings. Today the Reactjs framework has matured significantly but we still see different approach to write React Component. For my existing react application I have spent more than seven months to write react components and experienced different approaches within my team and now we feel in a good state with our existing practices. Here I would like to share our best practices. I hope this will be beneficial for beginner or experienced.

React Components

Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called "props") and return React elements describing what should appear on the screen.

Functional Components

The simplest way to define a component is to write a JavaScript function:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

This function is a valid React component because it accepts a single "props" object argument with data and returns a React element. We call such components "functional" because they are literally JavaScript functions. A good rule of thumb:

  • Use an ES6 class to define a component if you want local state feature or extra lifecycle hooks of component.
  • Complex or Big component should split into smaller components.
  • If a part of your UI is used several times (Button, Panel, Modal), or is complex enough on its own (user information, Comment), it is a good candidate to be a reusable component.

You can convert a functional component like Team to a class in five steps:

  1. Create an ES6 classwith the same name that extends Component.
  2. Add a single empty method to it called render().
  3. Move the body of the function into the render()
  4. Replace propswith props in the render() body.
  5. Delete the remaining empty function declaration.
// Functional Component

function Team(props) {
    const {team} = props
    return (
      <ul>
        {team.map((member, i) =>
          <TeamRow member={member} key={i} />
        )}
      </ul>
     )
}
// Class Component

class Team extends Component {
    render() {
    const {team} = this.props
    return (
        <ul>
	  {team.map((member, i) => 
	      <TeamRow member={member} key={i} />
           )}
        </ul>
    )
  }
}

By use of Class based Component we can use additional features such as local state and lifecycle hooks.

Importing Components and Libraries

import React, {Component, PropTypes} from 'react'
import {connect} from 'react-redux'
import cx from 'classnames'
import _ from 'lodash'
import {ProgressButton} from '../../components/common'
import Loader from '../loader/simple'
import actionCreators from './actions'

Redux Connect Component

Redux is a predictable state container for JavaScript apps. It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger. Redux Connect connects a React component to a Redux store.

import actionCreators from './actions'
import * as resultActions from '../results/actions'

const mapState = (state) => {
    return {
        results: state.results,
        navigation: state.navigation
    }
}

const mapActions = {
    ...actionCreators,
    loadResults: resultActions.default.load
}

@connect(mapState, mapActions)

Initializing State and setState

The state contains data specific to this component that may change over time. The state is user-defined, and it should be a plain JavaScript object. We also make sure to export our class as the default. Default exports (only one per script / module).

export default class AnnualResult extends Component {
    constructor(props) {
        super(props)
        this.state = {
            isLoading: true,
            comment: ''
        }
    }
}

It is good to use a new ES7 way of setting state.

state = { isLoading: true, comment: ''}

Using setState

  • setState(), React merges the object you provide into the current state.
  • setState(), is asynchronous
  • setState(), causes unnecessary renders
  • setState(), is not sufficient to capture all component state

It is always best practice to use setState() in component at minimum or instead of class component  use functional component if we do not need local state or class component lifecycle hooks.

Use immutable states

Immutable object is an object whose state cannot be modified after it is created. Immutable objects can save us all a headache and improve the rendering performance with their reference-level equality checks.

propTypes and defaultProps

React has some built-in typechecking abilities. To run typechecking on the props for a component, you can assign the special propTypesproperty:

static propTypes = {
    name: PropTypes.string,
    isEditing: PropTypes.bool.isRequired
}

When an invalid value is provided for a prop, a warning will be shown in the JavaScript console. You can define default values for your props by assigning to the special defaultPropsproperty:

static defaultProps = {
    isEditing: false
}

The defaultProps will be used to ensure that this.props.isEditing will have a value if it was not specified by the parent component. The propTypes typechecking happens after defaultProps are resolved, so typechecking will also apply to the defaultProps.

Adding Lifecycle Methods to a Class Component

We can declare special methods on the component class to run some code when a component mounts and unmounts: Runs after the component output has been rendered to the DOM.

componentDidMount() {
}

Runs during the component will render to the DOM for the first time.

componentWillMount() {
}

It is invoked immediately before a component is unmounted and destroyed.

componentWillUnmount() {
}

It is invoked before a mounted component receives new props.

componentWillReceiveProps(nextProps) {
}

Methods

commentChange = (e) => {
    this.setState({comment: e.target.value})
}

isConfirmed = () => {
    return this.props.confirmed
}

handleSubmit = (e) => {
    e.preventDefault()
    this.props.save()
}

With class components, when you pass methods to subcomponents, you have to ensure that they have the right this when they’re called. This is usually achieved by passing this.handleSubmit.bind(this) to the subcomponent. We think this approach is cleaner and easier, maintaining the correct context automatically via the ES6 arrow function.

Destructuring Props

const {isLoading, isEditing, data, editable} = this.props

Closures

Avoid passing new closures to subcomponents, like so: Use the below:

onChange={this.handleChange} placeholder="Your Name"/>

Wrapping

const InError = (props) => (
    <div>
        <p>Unfortunately, an error has occurred.</p>
    </div>
)

InError.propTypes = {
    summary: React.PropTypes.string,
    details: React.PropTypes.string
}

export default InError

When you only want to render an element on one condition, instead of doing this…

{ isLoading ? <p>loading</p> : <p>not loading<p/> }

… use short-circuit evaluation:

if (isLoading) return <Loader/>
return ( … )

Conclusion

Was this article useful? Have any feedback? Leave a comment below. Thanks for reading!

Shahid Hussain

Shahid Hussain is a frontend developer and UX Consultant living and working in Sweden. Shahid is specializes in JavaScript development and developed anything from WordPress websites to complex e-commerce JavaScript applications. Shahid can also sketch, from websites to apps and icons, even print material. He works on content-centric, and mobile products, as well as cross-portal user experiences. Photography, music and travelling can trigger his attention.