Skip to content

Instantly share code, notes, and snippets.

@garretttaco
Last active October 6, 2022 03:29
Show Gist options
  • Save garretttaco/f36ffd349cf9578e68aa2f3a00770c18 to your computer and use it in GitHub Desktop.
Save garretttaco/f36ffd349cf9578e68aa2f3a00770c18 to your computer and use it in GitHub Desktop.
React component template and guidelines.

React component templates and style guide

NOTE: This should be an ever changing document that is kept up to the date with the teams standard.

Class components

Things to note

  • import order is important. No spaces should be added in between import statement. It is not needed as an identifier between different "imports types". The order is as follows:
    1. Libraries (react, prop-types, lodash, etc.)
    2. Utility functions (lib/axios, utils/doSomeCommonAction)
    3. Components
    4. Containers
  • When testing components, it is helpful to provide a named export of the component declaration to avoid having to deal with HoCs like react-redux#compose. Then you can mock redux's mapped props and state and just test what you need on the component.
  • The order of the classes static properties should be preserved in the order of
    1. propTypes
    2. contextTypes
    3. childContextTypes
    4. defaultProps
  • The use of constructors are not needed.
    • If you need to add state, declare it inline as a class property.
    • If you need to bind a method, declare it as a class property and assign an arrow function to it to which auto binds the this keyword.
  • propTypes should be defined as an object property of the functional component.
  • defaultProps should be defined for any non required props.
  • Lifecycle method ordering portrayed in template, should persist.
  • Overall ordering, for all class properties and methods, portrayed in template should persist.
  • Event handler convention.
    • Should follow the pattern on.
    • Should be arrow functions assigned as class properties.
    • Should generally not be an inline function in render.
  • Getter methods
    • Returns computed data based information that this component is already aware of (props and state). No xhr requests should be made here.
  • Optional render methods
    • The naming pattern should be render
    • Be mindful when using this, it may be an identifier that you have an opportunity to break out this UI into another component.
  • Render method should always be the last method declared.
  • The default export is where we decorate our component with HoCs. This should happen at the bottom of the file.

Template

// Libraries (react, prop-types, lodash, etc.)
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// Utility functions (lib/axios, utils/doSomeCommonAction)
// Containers
// Components

export class MyComponent extends Component {
    static propTypes = {};

    static contextTypes = {};

    static childContextTypes = {};

    static defaultProps = {};

    state = {};

    getChildContext() {}

    componentWillMount() {}

    componentDidMount() {}

    componentWillReceiveProps() {}

    shouldComponentUpdate() {}

    componentWillUpdate() {}

    componentDidUpdate() {}

    componentWillUnmount() {}

    // Event handlers
    onClickDescriptor = ev => {};

    onChangeDescriptor = ev => {};

    // Getter methods
    getDescriptor() {}

    // Optional render methods
    renderDescriptor() {}

    render() {
        return null;
    }
}

export default MyComponent;

Functional component

Things to note

  • import order is important. No spaces should be added in between import statement. It is not needed as an identifier between different "imports types". The order is as follows:
    1. Libraries (react, prop-types, lodash, etc.)
    2. Utility functions (lib/axios, utils/doSomeCommonAction)
    3. Components
    4. Containers
  • When testing components, it is helpful to provide a named export of the component declaration to avoid having to deal with HoCs like react-redux#compose. Then you can mock redux's mapped props and state and just test what you need on the component.
  • All event handlers should be passed down from props and not declared as inline functions.
  • propTypes should be defined as an object property of the functional component.
  • defaultProps should be defined for any non required props.

Template

// Libraries (react, prop-types, lodash, etc.)
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// Utility functions (lib/axios, utils/doSomeCommonAction)
// Containers
// Components

export function MyFunctionalComponent(props) {
    return <Button onClick={props.onClickHandleAction} />;
}

MyFunctionalComponent.propTypes = {};
MyFunctionalComponent.defaultProps = {};

export default MyFunctionalComponent;

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.

Props

  • Always use camel case for prop names

      <Button handleClick={this.onClickButton} />
      // vs
      <Button handle-click={this.onClickButton} />
  • Do not pass static true values to boolean props.

      <Button disabled />
      // vs
      <Button disabled={true} />
  • Spread props:

    • Use spread props sparingly and filter out unnecessary props when possible.
    • Should we use prop-types-exact?
    • All HoCs should spread props down to WrappedComponent.

Class vs createClass vs functional components

  • We use ES6 class syntax and no longer write new components with the createClass API since it no longer available in React 16+.

  • If you don't need state, context, or refs, prefer normal functions over classes.

  • Classes should use static properties instead of adding them after the class declaration.

    // bad
    class MyComponent extends Component {}
    
    MyComponent.propTypes = {};
    
    // good
    class MyComponent extends Component {
        static propTypes = {};
    }

Mixins

  • Mixins do not work with ES6 classes, thus are not to be used for any new components.

Naming

  • Extensions: Use .js extension for React components.

  • Methods Do not use underscore prefix for internal methods of a React component. Method names should be camelCase.

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

  • Reference Naming: Use PascalCase for React components and camelCase for their instances.

    // 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';
  • Higher-order Component Naming: HoCs should be named with the convention of with and be camelCase. If the returned component is a class, then the name should be same name as the outer function and should be PascalCase, so With. Use a composite of the higher-order component's name and the passed-in component's name as the displayName on the generated component. For example, the higher-order component withFoo(), when passed a component Bar should produce a component with a displayName of withFoo(Bar).

    Why? A component's displayName may be used by developer tools or in error messages, and having a value that clearly expresses this relationship helps people understand what is happening.

    // bad
    export default function withFoo(WrappedComponent) {
      return function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    }
    
    // good
    export default function withFoo(WrappedComponent) {
      function WithFoo(props) {
        return <WrappedComponent {...props} foo />;
      }
    
      const wrappedComponentName = WrappedComponent.displayName
        || WrappedComponent.name
        || 'Component';
    
      WithFoo.displayName = `withFoo(${wrappedComponentName})`;
      return WithFoo;
    }
  • Props Naming: Avoid using DOM component prop names for different purposes.

    Why? People expect props like style and className to mean one specific thing. Varying this API for a subset of your app makes the code less readable and less maintainable, and may cause bugs.

    // bad
    <MyComponent style="fancy" />
    
    // bad
    <MyComponent className="fancy" />
    
    // good
    <MyComponent variant="fancy" />

References

I drew inspiration, content and examples from the AirBnb React style guide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment