Skip to content
This repository has been archived by the owner on Nov 8, 2019. It is now read-only.

Latest commit

 

History

History
 
 

react

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 

HubSpot React/JSX Style Guide

Forked from Airbnb's React/JSX Style Guide

HubSpot's version of a mostly reasonable approach to React and JSX

Table of Contents

  1. Basic Rules
  2. Class vs React.createClass vs stateless
  3. Naming
  4. Declaration
  5. Alignment
  6. Quotes
  7. Spacing
  8. Props
  9. Parentheses
  10. Tags
  11. Methods
  12. Ordering
  13. isMounted
  14. Set State
  15. Guidelines

Basic Rules

  • Only include one React component per file.
  • Always use JSX syntax.
  • Do not use React.createElement unless you're initializing the app from a file that is not JSX.

Class vs React.createClass vs stateless

  • If you have internal state and/or refs, prefer class extends React.Component over React.createClass unless you have a very good reason to use mixins. eslint: react/prefer-es6-class react/prefer-stateless-function

    // bad
    const Listing = React.createClass({
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    });
    
    // good
    class Listing extends React.Component {
      // ...
      render() {
        return <div>{this.state.hello}</div>;
      }
    }

    And if you don't have state or refs, prefer normal functions (not arrow functions) over classes:

    // bad
    class Listing extends React.Component {
      render() {
        return <div>{this.props.hello}</div>;
      }
    }
    
    // bad (since arrow functions do not have a "name" property)
    const Listing = ({ hello }) => (
      <div>{hello}</div>
    );
    
    // good
    function Listing({ hello }) {
      return <div>{hello}</div>;
    }

Naming

  • Extensions: Use .js extension for React components.

  • Filename: Use PascalCase for filenames. E.g., ReservationCard.js.

  • Reference Naming: Use PascalCase for React components and camelCase for their instances. eslint: react/jsx-pascal-case

    // bad
    import reservationCard from './ReservationCard';
    
    // good
    import ReservationCard from './ReservationCard';
    
    // bad
    const ReservationItem = <ReservationCard />;
    
    // good
    const reservationItem = <ReservationCard />;
  • Component Naming: Use the filename as the component name. For example, ReservationCard.js should have a reference name of ReservationCard. However, for root components of a directory, use index.js as the filename and use the directory name as the component name:

    // bad
    import Footer from './Footer/Footer';
    
    // bad
    import Footer from './Footer/index';
    
    // good
    import Footer from './Footer';

Declaration

  • Do not use displayName for naming components. Instead, name the component by reference.

    // bad
    export default React.createClass({
      displayName: 'ReservationCard',
      // stuff goes here
    });
    
    // good
    export default class ReservationCard extends React.Component {
    }

Alignment

  • Follow these alignment styles for JSX syntax. eslint: react/jsx-closing-bracket-location

    // bad
    <Foo superLongParam="bar"
         anotherSuperLongParam="baz" />
    
    // good
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    />
    
    // if props fit in one line then keep it on the same line
    <Foo bar="bar" />
    
    // children get indented normally with closing brackets either `after-props`
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz">
      <Spazz />
    </Foo>
    
    // or `tag-aligned`
    
    <Foo
      superLongParam="bar"
      anotherSuperLongParam="baz"
    >
      <Quux />
    </Foo>

Quotes

  • Always use double quotes (") for JSX attributes, but single quotes for all other JS. eslint: jsx-quotes

Why? JSX attributes can't contain escaped quotes, so double quotes make conjunctions like "don't" easier to type. Regular HTML attributes also typically use double quotes instead of single, so JSX attributes mirror this convention.

```javascript
// bad
<Foo bar='bar' />

// good
<Foo bar="bar" />

// bad
<Foo style={{ left: "20px" }} />

// good
<Foo style={{ left: '20px' }} />

// good
<Foo bar='"bar"' />
```

Spacing

  • Always include a single space in your self-closing tag.

    // bad
    <Foo/>
    
    // very bad
    <Foo                 />
    
    // bad
    <Foo
     />
    
    // good
    <Foo />

Props

  • Always use camelCase for prop names.

    // bad
    <Foo
      UserName="hello"
      phone_number={12345678}
    />
    
    // good
    <Foo
      userName="hello"
      phoneNumber={12345678}
    />
  • Use explicit values for Boolean props. eslint: react/jsx-boolean-value

    // bad (implicit true)
    <Hello personal />;
    
    // super-bad (string value)
    <Hello personal="true" />;
    
    // good (explicit)
    <Hello personal={true} />;
  • Declare every prop in propTypes. If you use a value on this.props anywhere in your component, it should be listed in propTypes.

    // bad (missing propType declaration)
    export default React.createClass({
      render() {
        return <div>Hello {this.props.name}</div>;
      }
    });
    
    // good (all props declared)
    export default React.createClass({
      propTypes: {
        name: React.PropTypes.string,
        children: React.PropTypes.node
      },
    
      render() {
        const {children, name} = this.props;
        return (
          <div>
            Hello {name}
            {children}
          </div>
        );
      }
    });

Parentheses

  • Wrap JSX tags in parentheses when they span more than one line. eslint: react/wrap-multilines

    // bad
    render() {
      return <MyComponent className="long body" foo="bar">
               <MyChild />
             </MyComponent>;
    }
    
    // good
    render() {
      return (
        <MyComponent className="long body" foo="bar">
          <MyChild />
        </MyComponent>
      );
    }
    
    // good, when single line
    render() {
      const body = <div>hello</div>;
      return <MyComponent>{body}</MyComponent>;
    }

Tags

  • Always self-close tags that have no children. eslint: react/self-closing-comp

    // bad
    <Foo className="stuff"></Foo>
    
    // good
    <Foo className="stuff" />
  • If your component has multi-line properties, close its tag on a new line. eslint: react/jsx-closing-bracket-location

    // bad
    <Foo
      bar="bar"
      baz="baz" />
    
    // good
    <Foo
      bar="bar"
      baz="baz"
    />

Methods

  • Use arrow functions to close over local variables.

    function ItemList(props) {
      return (
        <ul>
          {props.items.map((item, index) => (
            <Item
              key={item.key}
              onClick={() => doSomethingWith(item.name, index)}
            />
          ))}
        </ul>
      );
    }
  • Bind event handlers for the render method in the constructor. eslint: react/jsx-no-bind

Why? A bind call in the render path creates a brand new function on every single render.

```javascript
// bad
class extends React.Component {
  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv.bind(this)} />
  }
}

// good
class extends React.Component {
  constructor(props) {
    super(props);

    this.onClickDiv = this.onClickDiv.bind(this);
  }

  onClickDiv() {
    // do stuff
  }

  render() {
    return <div onClick={this.onClickDiv} />
  }
}
```
  • Do not use underscore prefix for internal methods of a React component.

    // bad
    React.createClass({
      _onClickSubmit() {
        // do stuff
      },
    
      // other stuff
    });
    
    // good
    class extends React.Component {
      onClickSubmit() {
        // do stuff
      }
    
      // other stuff
    }

Ordering

  • Ordering for class extends React.Component:
  1. constructor
  2. optional static methods
  3. getChildContext
  4. componentWillMount
  5. componentDidMount
  6. componentWillReceiveProps
  7. shouldComponentUpdate
  8. componentWillUpdate
  9. componentDidUpdate
  10. componentWillUnmount
  11. Getters, setters, event handlers, helper methods, etc.
  12. Optional render methods like renderNavigation() or renderProfilePicture()
  13. render
  • How to define propTypes, defaultProps, contextTypes, etc...

    import React, { PropTypes } from 'react';
    
    const propTypes = {
      id: PropTypes.number.isRequired,
      url: PropTypes.string.isRequired,
      text: PropTypes.string,
    };
    
    const defaultProps = {
      text: 'Hello World',
    };
    
    class Link extends React.Component {
      static methodsAreOk() {
        return true;
      }
    
      render() {
        return <a href={this.props.url} data-id={this.props.id}>{this.props.text}</a>
      }
    }
    
    Link.propTypes = propTypes;
    Link.defaultProps = defaultProps;
    
    export default Link;
  • Ordering for React.createClass: eslint: react/sort-comp

  1. displayName
  2. mixins
  3. propTypes
  4. contextTypes
  5. childContextTypes
  6. statics
  7. defaultProps
  8. getDefaultProps
  9. getInitialState
  10. getChildContext
  11. componentWillMount
  12. componentDidMount
  13. componentWillReceiveProps
  14. shouldComponentUpdate
  15. componentWillUpdate
  16. componentDidUpdate
  17. componentWillUnmount
  18. Getters, setters, event handlers, helper methods, etc.
  19. Optional render methods like renderNavigation() or renderProfilePicture()
  20. render

isMounted

Why? isMounted is an anti-pattern, is not available when using ES6 classes, and is on its way to being officially deprecated.

eslint rules: react/no-is-mounted.

Set State

  • Don't setState in componentDidMount

    Updating the state after a component mount will trigger a second render() call and can lead to property/layout thrashing. This does not apply to setState in event handlers added here.

    If you need to do something to change state here, use a function prop that can change state at the top level and pass new data down accordingly.

  • Don't setState in componentDidUpdate

    Updating the state after a component update will trigger a second render() call and can lead to property/layout thrashing.

    If you need to do something to change state here, use a function prop that can change state at the top level and pass new data down accordingly.

GuideLines

These are guidelines more than rules, and they will likely be more controversial than those listed above. This is where we collaborate :)

  • Destructure props and state variables at the top of render()

    Instead of spreading your state access all over the place, destructure everything at the top of render to make it obvious which data fields are used.

    // bad
    export default React.createClass({
      ...
      render() {
        return (
          <div>
            {/* access spread throughout component */}
            Hello {this.props.name}
            {/* stateful helpers... */}
            {this.renderHelper()}
            {/* transfers props that are intended for this component */}
            <CustomComponent {...this.props} />
          </div>
        );
      }
    });
    
    // good
    export default React.createClass({]
      ...
      render() {
        {/* all data in one place */}
        const {children, name, ...other} = this.props;
        return (
          <div>
            Hello {name}
            {/* stateless helper functions */}
            {this.renderHelper(children)}
            {/* only necessary props transferred */}
            <CustomComponent {...other} />
          </div>
        );
      }
    });

    This has the additional benefit of:

    • Keeping your helper methods stateless by passing in values (see below)
    • Enabling props transfer using ... for unused values without triggering warnings for unused vars in render
  • Keep your helper methods stateless

    Prefer to pass props/state values into your helper methods instead of access via this in the method body. This enables you to extract them to helper objects without refactoring, as well as easily write automated tests without needing to setup a complete stateful component.

    // discouraged (stateful method, harder to test and extract/refactor)
    export default React.createClass({
      ...
      render() {
        return (
          <div>
            {this.renderHelper()}
          </div>
        );
      },
    
      renderHelper() {
        return `Custom value: ${someRenderingLogic(this.props.value)}`;
      }
    });
    
    
    // preferred (stateless function, easy to test and extract/refactor)
    export default React.createClass({]
      ...
      render() {
        const {value} = this.props;
        return (
          <div>
            {this.renderHelper(value)}
          </div>
        );
      },
    
      renderHelper(value) {
        return `Custom value: ${someRenderingLogic(value)}`;
      }
    });

⬆ back to top