React

Syntactic rules

Principles

Syntax

  1. As with the base config we use Prettier for familiar formatting defaults.
  2. Making JSX more compact. JSX is similar to HTML and tend to extend by width. We use rules which help make our markup narrower. E.g., react/jsx-boolean-value.
  3. Minimizing the count of symbols and their kinds. JSX (as HTML too) has very noisy syntax. We try to omit unnecessary and optional symbols and prefer to use more compact syntax. When we need to use some decorative symbols, we prefer more consistent syntax. E.g., react/jsx-no-useless-fragment, react/jsx-tag-spacing.
  4. We try not to affect the look of JSX code too much so we don't force the use of specific form of syntax. E.g., we don't use react/jsx-sort-props. [~]

Semantics

  1. Explicit declaration React semantics rules. React and JSX have many specific semantic rules, the violation of which leads to error. For example react hooks, lifecycle, component declarations have many implicit rules which has no meaning in Vanilla JS context. But if this rules is breaked, react will works incorrectly. Because JS interpreter(and base linting rules for JS) doesn't know anything about state, props, lifecycle, hooks and so on. We declare this rules in linting and help base lint rules(as "no-unused-vars") works with React syntax/semantics. E.g., react-hooks/rules-of-hooks, react/no-typos, react/jsx-no-constructed-context-values, react/no-access-state-in-setstate, etc.
  2. Prevent usage of constructions which break a11y or web security. We use config jsx-a11y/recommended and rule react/no-danger.
  3. Prevent usage of deprecated or unconventional API. Most of the rules covering this issue are included in recommended config, additionally we enable react/no-unsafe.
  4. Optimize React for performance. React's performance relies on change detection of props by reference. To make it work, we should avoid several constructions which lead to the generation of new references. E.g., react/prefer-stateless-function, react/no-array-index-key
  5. Enforce usage of prop-types in non-TS projects. We prefer to use props-types because it is a standardized way to declare component API. That's why we enable rules helping to write better prop types: e.g., react/prop-types, react/forbid-prop-types, react/no-unused-prop-types etc.
  6. Don't specify naming for React props. Though React code has typical props templates (handlers, boolean flags, etc), we don't enforce naming convention for them. Naming convention must be project-specific so we don't use rules like react/boolean-prop-naming or react/jsx-handler-names. We still use react/jsx-pascal-case to encourage consistent component naming between projects.
  7. Don't enforce conventions about structure of your project and components. All these conventions are be project-specific and must be defined by project developers. We don't use rules like react/static-property-placement, react/no-multi-comp or react/jsx-max-depth. [~]

React rules

As a base we use recommended configs from eslint-plugin-react and eslint-plugin-react-hooks. [~]

react/button-has-type

// Fail
return <button>Hello</button>

// Pass
return <button type="button">Hello</button>

[~]

react/default-props-match-prop-types

// Fail
MyComponent.propTypes = {
  foo: React.PropTypes.string.isRequired,
}
MyComponent.defaultProps = {
  foo: "foo"
}

// Pass
MyComponent.propTypes = {
  foo: React.PropTypes.string.isRequired,
}

// Pass
MyComponent.propTypes = {
  foo: React.PropTypes.string,
}
MyComponent.defaultProps = {
  foo: "foo"
}

[~]

react/forbid-prop-types

// Fail
Component.propTypes = {
  a: PropTypes.any,
  b: PropTypes.array,
  c: PropTypes.object
}

// Pass
Component.propTypes = {
  a: PropTypes.objectOf(PropTypes.number),
  b: PropTypes.arrayOf(PropTypes.string),
  c: PropTypes.shape({
    c1: PropTypes.number,
    c2: PropTypes.string,
  })
}

[~]

react/function-component-definition

We prefer components as arrow function for the sake of homogeneity. Benefit of arrow function over function expression is inability to use this.

// Fail
export function MyComponent() {}

// Pass
export const MyComponent = () => {}

[~]

react/jsx-boolean-value

// Fail
return <Component disabled={true} />

// Pass
return <Component disabled />

[~]

react/jsx-fragments

// Fail
return <React.Fragment>...</React.Fragment>

// Pass
return <>...</>
return <React.Fragment key="key">...</React.Fragment>

[~]

react/jsx-no-constructed-context-values

// Fail
return (
  <MyContext.Provider value={{ foo }}>
    ...
  </MyContext.Provider>
)

// Pass
const value = useMemo(() => ({ foo }), [foo])
return (
  <MyContext.Provider value={value}>
    ...
  </MyContext.Provider>
)

[~]

react/jsx-pascal-case

// Fail
return <My_component />
return <MY_COMPONENT />

// Pass
return <MyComponent />

[~]

react/no-access-state-in-setstate

// Fail
this.setState({ value: this.state.value + 1 })

// Pass
this.setState(state => ({ value: state.value + 1 }))

[~]

react/no-array-index-key

In a few cases when only index is available as a key, rule can be disabled for a specific line.

// Fail
return items.map((item, index) => (
  <Item key={index} />
))

// Pass
return items.map((item, index) => (
  <Item key={item.id} />
))

[~]

react/no-danger

// Fail
return <div dangerouslySetInnerHTML={{ __html: 'Hello World' }}></div>

[~]

react/no-typos

// Fail
class MyComponent extends React.Component {
  static PropTypes = {
    a: PropTypes.bol
  }
  static defaultprops = {}
  getDerivedStateFromProps() {}
  ComponentWillMount() {}
  componentdidupdate() {}
}

// Pass
class MyComponent extends React.Component {
  static propTypes = {
    a: PropTypes.bool
  }
  static defaultProps = {}
  static getDerivedStateFromProps() {}
  componentWillMount() {}
  componentDidUpdate() {}
}

[~]

react/no-unsafe

// Fail
class MyComponent extends React.Component {
  UNSAFE_componentWillMount() {}
  UNSAFE_componentWillReceiveProps() {}
  UNSAFE_componentWillUpdate() {}
}

[~]

react/no-unused-prop-types

// Fail
const MyComponent = ({ firstName }) => <span>{firstName}</span>
MyComponent.propTypes = {
  firstName: PropTypes.string,
  lastName: PropTypes.string,
}

// Pass
const MyComponent = ({ firstName, lastName }) => <span>{firstName} {lastName}</span>
MyComponent.propTypes = {
  firstName: PropTypes.string,
  lastName: PropTypes.string,
}

[~]

react/prefer-stateless-function

Though we prefer to use functional components with hooks over class components, we don't enforce it for all the components, only for class components with no state or lifecycle methods.

// Fail
class MyComponent extends React.Component {
  render() {
    return <span>{this.props.name}</span>
  }
}

// Pass
const MyComponent = ({ name }) => <span>{name}</span>

[~]

react/self-closing-comp

// Fail
return <MyComponent></MyComponent>

// Pass
return <MyComponent />

[~]

[~]

react/display-name

Disabled this rule as it gives false positives for render props. Also we prefer named exports which guarantees that exported component functions have proper names. [~]

react/react-in-jsx-scope

Disabled this rule as it is common to have React as a global variable (e.g., out-of-the-box in Next.js or via webpack's ProvidePlugin). [~]

react-hooks/exhaustive-deps

This rule may trigger a lot of unwanted errors when useEffect is used as watcher. It is recommended to manually enable this rule inside files which heavily rely on memoization. [~]

JSX A11y rules

Using plugin:jsx-a11y/recommended config as a starting point. [~]

[~]

jsx-a11y/accessible-emoji

Deprecated [~]

jsx-a11y/no-onchange

Deprecated [~]

jsx-a11y/label-has-associated-control

Disabled until this issue is fixed. [~]

jsx-a11y/no-autofocus

It may be considered OK to use autofocus on pages which consist only of form (e.g., login page). In other cases usage of autofocus may lead to a confusion of a screen reader user. We leave the decision to use autofocus to developers on a case-by-case basis. [~]

[~]

jsx-a11y/anchor-is-valid

// Fail
return <a onClick={foo}>Perform action</a>

// Pass
return <a href="/some/valid/uri">Navigate to page</a>

This rule may lead to a lot of errors when linting Next.js projects so consider either turning it off or following these recommendations. [~]

jsx-a11y/mouse-events-have-key-events

If an element has some logic tied to hover it should be duplicated via focus/blur keyboard events. It is important for both purely visual changes (e.g., highlighting) and actual page changes (e.g., showing hint).

// Fail
return <div onMouseOver={() => {}} />

// Pass
return (
  <div
    tabindex={0}
    onMouseOver={() => {}}
    onFocus={() => {}}
  />
)

[~]

jsx-a11y/anchor-has-content

This rule is always linting <a> tags. Additionally we configured this rule for <Link> component. If you have other custom link components configure this rule inside your project specifying the name of your custom link component.

// Fail
return <a />
return <Link />

// Pass
return <a>Link content</a>
return <Link>Link content</Link>

[~]