Skip to content

Latest commit

 

History

History

react

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

import {React} from 'Shopify'

This guide provides a few guidelines on writing sensible React. Many of these rules are enforced by our shared ESLint configuration, which makes use of the excellent eslint-plugin-react project.

Table of contents

  1. Naming
  2. Whitespace
  3. Punctuation
  4. Components
  5. Props
  6. Modules
  7. Resources

Naming

  • 1.1 Always use a .js file extension, even when that file contains JSX.

  • 1.2 React components are always named using PascalCase. Use camelCase for their instances.

    ESLint rule: jsx-pascal-case

    // bad
    class financialReport extends Component {}
    let MyReport = <financialReport />;
    
    // good
    class FinancialReport extends Component {}
    let myReport = <FinancialReport />;
    • Use camelCase for all props.
    // bad
    class BadComponent extends Component {
      static propTypes = {my_prop: PropTypes.bool}
    }
    
    // good
    class GoodComponent extends Component {
      static propTypes = {myProp: PropTypes.bool}
    }

↑ scrollTo('#table-of-contents')

Whitespace

  • 2.1 When there are no children for a component, use the self-closing tag. When using a self-closing tag for a component written on a single line, leave a single space between the last character and the closing of the tag.

    ESLint rules: self-closing-comp, jsx-space-before-closing

    // bad
    <BadOne></BadOne>
    <BadTwo/>
    <BadThree    />
    <BadFour
      />
    
    // good
    <Good />
  • 2.2 When writing a component that has multiple props, put a single prop per line, with two spaces of indentation, and a closing tag on a newline that aligns with the opening tag.

    ESLint rules: jsx-closing-bracket-location and jsx-first-prop-new-line

    // bad
    <BadOne aLongPropName="a little too long"
            anotherLongishPropName />
    
    <BadTwo
      aLongPropName="a little too long"
      anotherLongishPropName>
        <SubComponent>
    </BadTwo>
    
    // good
    <GoodOne
      aLongPropName="a little too long"
      anotherLongishPropName
    />
    
    <GoodTwo
      aLongPropName="a little too long"
      anotherLongishPropName
    >
      <SubComponent>
    </GoodTwo>
  • 2.3 Never include spaces within the curly braces of JSX properties. Values within JSX properties should adhere to the same rules as if they appeared anywhere else in JavaScript (no interior spaces for object braces or brackets, interior space for function braces).

    ESLint rule: jsx-curly-spacing

    // bad
    <BadOne prop={ () => {console.log('callback');} } />
    <BadTwo prop={[ 1, 2, 3 ]} />
    <BadThree prop={{ foo: 'bar' }} />
    
    // good
    <BadOne prop={() => { console.log('callback'); }} />
    <BadTwo prop={[1, 2, 3]} />
    <BadThree prop={{foo: 'bar'}} />
  • 2.4 Do not place a space on either side of the = in prop declarations.

    ESLint rule: jsx-equals-spacing

    // bad
    <Bad status = "is not very good" />
    
    // good
    <Good status="is great!" />
  • 2.5 When props span over multiple lines, list one prop per line (with no props on the same line as the opening tag).

    ESLint rule: jsx-first-prop-new-line

    // bad
    <Bad status="is not very good"
      redeemable={false}
    />
    
    <Bad
      status="is not very good" redeemable={false}
    />
    
    // good
    <Good status="is great!" needsWork={false} />
    
    <Good
      status="is great!"
      needsWork={false}
    />

↑ scrollTo('#table-of-contents')

Punctuation

  • 3.1 Use double quotes for JSX attributes.

    Why? Regular HTML attributes (and, in general, XML) typically use double quotes, so JSX attribute names follow this convention.

    ESLint rule: jsx-quotes

    // bad
    <Bad prop='please, no!' />
    <Bad propWithJS={{left: "20px"}} />
    
    // good
    <Good prop="please, no!" />
    <Good propWithJS={{left: '20px'}} />
  • 3.2 When JSX spans multiple lines, wrap them in parentheses. When on a single line, do not wrap in parentheses.

    ESLint rule: wrap-multilines

    // bad
    render() {
      return <BadOne hasLongProp="sure does">
               <SomeChildComponent />
             </BadOne>
    }
    
    render() {
      let {children} = this.props;
      return (<BadTwo>{children}</BadTwo>);
    }
    
    // good
    render() {
      return (
        <GoodOne hasLongProp="sure does">
          <SomeChildComponent />
        </GoodOne>
      );
    }
    
    render() {
      let {children} = this.props;
      return <GoodTwo>{children}</GoodTwo>;
    }

↑ scrollTo('#table-of-contents')

Components

  • 4.1 Always extend React’s Component class rather than using React.createClass.

    Why? The class syntax is the method React is pushing going forward, and it’s how most tutorials are written.

    ESLint rule: prefer-es6-class

    // bad
    let BadComponent = React.createClass({
      render() {
        return <div />;
      },
    });
    
    // good
    // (you can also just extend `Component` if you explicitly imported it)
    class GoodComponent extends Component {
      render() {
        return <div />;
      }
    }
  • 4.2 Always use JSX syntax and avoid React.createElement.

    // bad
    class BadComponent extends Component {
      render() {
        return React.createElement('div', {foo: 'bar'});
      }
    }
    
    // good
    class GoodComponent extends Component {
      render() {
        return <div foo="bar" />;
      }
    }
  • 4.3 Prefer React’s stateless components for components that do not have any state or lifecycle hooks.

    Note: as of this writing, stateless components do not play nicely with React’s testing utilities. Until this is resolved, continue to extend Component for new components.

    Why? Such components are very easy to represent as pure functions, and doing so encourages more stateless components, which decreases application complexity.

    ESLint rule: prefer-stateless-function

    // bad
    export default class BadComponent extends Component {
      render() {
        let {children, hidden} = this.props;
        return <div style={{opacity: hidden ? 0 : 1}}>{children}</div>;
      }
    }
    
    // good
    export default function GoodComponent({children, hidden}) {
      return <div style={{opacity: hidden ? 0 : 1}}>{children}</div>;
    }
  • 4.4 Do not use underscore-prefixed methods in React components.

    Why? Most methods should either be lifecycle hooks, or callbacks, which are by definition not private since they are called by other components.

    // bad
    class BadComponent extends Component {
      _handleButtonClick() {}
    }
    
    // good
    class GoodComponent extends Component {
      handleButtonClick() {}
    }
  • 4.5 When overriding the constructor of Component, make sure to call super with all arguments passed to the constructor. It is easiest to do this using the rest/ spread operator (...).

    // bad
    class BadComponent extends Component {
      constructor() {
        super();
        this.doSomething();
      }
    }
    
    // good
    class GoodComponent extends Component {
      constructor(...args) {
        super(...args);
        this.doSomething();
      }
    }
  • 4.6 Prefer the handle prefix for callbacks passed to contained components.

    Why? The handle prefix clearly indicates its purpose, and differentiates it from on-prefixed properties which accept callbacks.

    // bad
    class BadComponent extends Component {
      onClickButton() {}
    
      render() {
        return <button onClick={this.onClickButton}>Button</button>;
      }
    }
    
    // good
    class GoodComponent extends Component {
      handleButtonClick() {}
    
      render() {
        return <button onClick={this.handleButtonClick}>Button</button>;
      }
    }
  • 4.7 Maintain a sensible ordering of methods in your component. At a high level, order the component as follows: statics and instance class properties, constructor, lifecycle methods, other methods (like handlers and helpers), and, finally, render (and any methods you have broken render up into).

    Why? A consistent ordering between components helps other developers find what they are looking for more quickly.

    ESLint rule: sort-comp

  • 4.8 Avoid violating the principles of React by doing things like directly setting state (use setState), setting state during the wrong lifecycle hooks (componentDidMount and componentDidUpdate), and checking whether your component is mounted.

    Why? These make your component harder to understand, and these features are either not supported or likely to be removed from future versions of React.

    ESLint rules: no-deprecated, no-did-mount-set-state, no-did-update-set-state, no-direct-mutation-state, no-is-mounted

↑ scrollTo('#table-of-contents')

Props

  • 5.1 Always include Flow types to annotate props. propTypes are also acceptable for projects not using Flow, particularly when they are also needed for contextTypes.

    Why? Flow types/ propTypes for props provide a great way to enforce the type requirements of your component's API, and serve as documentation of what the component can do.

    ESLint rule: prop-types

    // bad
    class BadComponent extends Component {
      render() {
        // what are these props?
        let {propOne, propTwo} = this.props;
      }
    }
    
    // good
    class GoodComponent extends Component {
      static propTypes = {
        propOne: PropTypes.bool,
        propTwo: PropTypes.string,
      }
    
      render() {
        let {propOne, propTwo} = this.props;
      }
    }
    
    // even better
    type Props = {
      propOne?: boolean,
      propTwo?: string,
    };
    
    class GoodComponent extends Component {
      props: Props;
    
      render() {
        let {propOne, propTwo} = this.props;
      }
    }
  • 5.2 Avoid generic propTypes that do not enforce type requirements, like array and any (or their Flow equivalents). Prefer more specific constraints, like arrayOf.

    ESLint rule: forbid-prop-types

    // bad
    class BadComponent extends Component {
      static propTypes = {choices: PropTypes.array};
    }
    
    // good
    class BadComponent extends Component {
      static propTypes = {
        choices: PropTypes.arrayOf(PropTypes.string),
      };
    }
  • 5.3 Where it makes sense, default boolean props to being false.

    Why? Opt-in APIs are typically easier to understand and test. It also allows you to make better use of the simpler boolean attributes detailed below.

    // bad
    class BadComponent extends Component {
      props: {visible: boolean};
      static defaultProps = {visible: true};
    }
    
    let shouldBeHidden = <BadComponent visible={false} />
    
    // good
    class GoodComponent extends Component {
      props: {visible: boolean};
    
      // Or just omit because an unset value is falsey
      static defaultProps = {hidden: false};
    }
    
    let shouldBeHidden = <GoodComponent hidden />
  • 5.4 For functional React components, use default values in the function’s parameters rather than setting defaultProps.

// bad
function BadComponent({disabled}) {
  return <button disabled={disabled}>Button</button>;
}

BadComponent.defaultProps = {disabled: false};

// good
function GoodComponent({disabled = false}) {
  return <button disabled={disabled}>Button</button>;
}
  • 5.5 Omit the value of a prop when it is explicitly true.

    Why? It reduces visual noise and matches how we would write similar attributes in HTML.

    ESLint rule: jsx-boolean-value

    // bad
    <Bad hidden={true} />
    
    // good
    <Good hidden />
    <Good hidden={false} />
  • 5.6 Omit curly braces when the prop is a string.

    Why? It is cleaner and matches how most attributes are defined in HTML.

    // bad
    <Bad status={"unnecessary braces"} />
    
    // good
    <Good status="nice and clean" />
  • 5.7 Name props that act as event listeners on<EventName>, and methods that are called in response to events handle<EventName>.

    // bad
    class BadComponent extends Component {
      static propTypes = {click: PropTypes.func}
    }
    
    class BadComponentUser extends Component {
      _onClick() {
        console.log('clicked');
      }
    
      render() {
        return <BadComponent click={this._onClick} />
      }
    }
    
    // good
    class GoodComponent extends Component {
      static propTypes = {onClick: PropTypes.func}
    }
    
    class GoodComponentUser extends Component {
      handleClick() {
        console.log('clicked');
      }
    
      render() {
        return <GoodComponent onClick={this.handleClick} />
      }
    }
  • 5.8 Avoid passing bound methods or arrow functions in the props of rendered components. Bind these in the constructor or use class properties instead.

    Why? Passing a bound function or using an arrow function creates a new function for each render, which increases memory usage and prevents using shallow rendering on subcomponents.

    ESLint rule: jsx-no-bind

    // bad
    class BadComponent extends Component {
      render() {
        return <button onClick={this.handleClick.bind(this)} onMouseEnter={() => this.handleMouseEnter()} />;
      }
    }
    
    // good
    class GoodComponent extends Component {
      constructor(...args) {
        super(...args);
        this.handleClick = this.handleClick.bind(this);
      }
    
      handleClick() {
        console.log('clicked');
      }
    
      render() {
        return <button onClick={this.handleClick} />;
      }
    }
    
    // also good
    class GoodComponent extends Component {
      handleClick = this.handleClick.bind(this);
    
      handleClick() {
        console.log('clicked');
      }
    
      render() {
        return <button onClick={this.handleClick} />;
      }
    }
    
    // also good
    class GoodComponent extends Component {
      handleClick = () => { console.log('clicked'); };
    
      render() {
        return <button onClick={this.handleClick} />;
      }
    }
  • 5.9 Components should not accept style or className as props. Prefer props that signal a particular variation on the component over allowing arbitrary customization.

    Why? Allowing custom styles or class names vastly increases the surface area of your component’s API. This makes the component harder to maintain since you must account for every set of class names, style objects, and their interplay with your component’s base styles. Meaningful variations are better suited to maintaining a design system, and make it simpler for consumers to use your component. This article provides additional details on why accepting classes or styles is a bad idea.

    ESLint rule: forbid-component-props

    // bad
    function BadComponent({className}) {
      return <div className={['my-component', className].filter(Boolean).join(' ')} />;
    }
    
    <BadComponent className="my-special-potentially-conflicting-classname" />
    
    // good
    function GoodComponent({special}) {
      return <div className={['my-component', special && 'my-component--special'].filter(Boolean).join(' ')} />;
    }
    
    <GoodComponent special />

↑ scrollTo('#table-of-contents')

Modules

  • 6.1 When importing from React, explicitly import the exact features you need (in addition to React, which will be a signal to the compiler to compile JSX).

    Why? Named imports explicitly indicate what you will be using at the top of the file, and reduce the duplication of accessing properties on React.

    // bad
    import React from 'react';
    
    class MyComponent extends React.Component {
      static contextTypes = {object: React.PropTypes.object};
    }
    
    // good
    import React, {Component, PropTypes} from 'react';
    
    class MyComponent extends Component {
      static contextTypes = {object: PropTypes.object};
    }
  • 6.2 Try to define only one React component per file. Name that file the same as the component it exports. The "main" component should be the default export from an index.js file in that directory, and any subcomponents can be exported as named exports from that same file.

    // in Card/Section.js
    export default class Section extends Component {}
    
    // in Card/Card.js
    export default class Card extends Component {}
    
    // in Card/index.js
    import Card from './Card';
    import Section from './Section';
    
    export default Card;
    export {Section as CardSection};

↑ scrollTo('#table-of-contents')

Resources

↑ scrollTo('#table-of-contents')