React Best Practices: A Comprehensive Coding Style Guide

Photo by Luca Bravo on Unsplash

React Best Practices: A Comprehensive Coding Style Guide

When developing React projects, it's essential to follow best practices to ensure code quality, maintainability, and consistency. While each project may have specific requirements, the following are some general practices that can be applied.

Basic Rules

  1. One Component Per File: Organize your code by including only one React component per file.

  2. Use Functional Components: Whenever possible, create functional components instead of class components. They are more concise and easier to maintain.

  3. Always Use JSX Syntax: Ensure that you consistently use JSX syntax when defining components.

Naming

File and Component Naming: Keep your file and component names identical. Use the PascalCase naming convention for both the filename and the component name. For example:

  • Bad: Filename: header.js
const Header = () => {}
export default Header;
  • Good: Filename: Header.js
const Header = () => {}
export default Header;

Import Ordering

Organize Imports: Arrange your import statements in the following order:

a. External package imports.

b. Slice imports.

c. Component imports.

d. Sibling imports.

e. CSS module import.

Component Ordering

Component Structure: Maintain a consistent structure for your components:

a. Import statements.

b. useContext declarations.

c. useSelector declarations.

d. useState declarations.

e. useEffect blocks.

f. Event handlers such as onClickSubmit or onChangeDescription.

g. Getter methods for rendering, e.g., getSelectReason or getFooterContent.

h. Optional render methods like renderNavigation or renderProfilePicture.

i. render (return statement with JSX).

j. PropTypes declaration.

k. Export statement.

Prop Types

  • Define propTypes and defaultProps: Specify propTypes and defaultProps as follows:
import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
  id: PropTypes.number.isRequired,
  url: PropTypes.string.isRequired,
  text: PropTypes.string,
};

const defaultProps = {
  text: 'Hello World',
};

const Link = ({ id, url, text }) => {
  return <a href={url} data-id={id}>{text}</a>
}

Link.propTypes = propTypes;
Link.defaultProps = defaultProps;

export default Link;

Alignment

  • Consistent Alignment: Maintain a uniform alignment for JSX attributes. For example:

  • Bad:

<Foo superLongParam="bar" anotherSuperLongParam="baz" />
  • Good:
<Foo superLongParam="bar" anotherSuperLongParam="baz" />
  • If the props fit in one line, keep them on the same line:
<Foo bar="bar" />
  • Children should be indented normally:
<Foo superLongParam="bar" anotherSuperLongParam="baz">
  <Spazz />
</Foo>

Quotes

  • Use Double Quotes for JSX Attributes: Always use double quotes (") for JSX attributes and single quotes for all other JavaScript. For example:
// bad 
<Foo bar='bar' /> 
// good 
<Foo bar="bar" /> 
// bad 
<Foo style={{ left: "20px" }} /> 
// good 
<Foo style={{ left: '20px' }} />

Prop Names

  • Use camelCase for Prop Names: Define prop names using camelCase:

    Tags

  • Self-Close Tags: Self-close tags that have no children:

// bad 
<Foo className="stuff"></Foo> 
// good 
<Foo className="stuff" />
  • Multi-Line Properties: If your component has multi-line properties, close its tag on a new line:
// bad
<Foo
    bar="bar"
    baz="baz" />

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

Stateless function components

For stateless components use the function syntax, introduced in React 0.14.

// Using an ES2015 (ES6) arrow function:
const Foo = (props) => {
    return <div>{props.bar}</div>;
};

// Or with destructuring and an implicit return, simply:
const Foo = ({ bar }) => (
    <div>{bar}</div>
);

// Then use: <Foo bar="baz" />

PropTypes declarations

  • Setting propTypes declarations is mandatory

  • Group them into required/none-required

  • Alphabetically sort each group

  • Separate them by a new line

static propTypes = { 
    blank: React.PropTypes.bool.isRequired, 
    block: React.PropTypes.bool.isRequired, 
    size: React.PropTypes.string.isRequired, 
    to: React.PropTypes.string.isRequired, 
    disabled: React.PropTypes.bool, 
};

Using handler methods

  • Name methods using 'handle' + triggering event, e.g. handleClick

  • Bind handler using the ES6 arrow syntax, so inside the callback, it has always the right context

const Foo = () => {
    const [isClicked, setIsClicked] = useState(false);
    handleClick = (e) => {
    setIsClicked(!isClicked);
    }

    return (
     <button onClick={handleClick}>Submit</button>
    );
}

Using “container” components for loading data from Stores

Container Components: Separate data loading logic into container components.

// CommentListContainer.js
const CommentListContainer = () => {
  const [comments, setComments] = useState([]);
  useEffect(() => {
    (async () => {
      const response = await fetch("/my-comments.json");
      const data = await response.json();
      setComments(data);
    })();
  }, []);

  return <CommentList comments={comments} />;
};

// CommentList.js

const CommentList = (props) => {
  const renderComment = ({ body, author }) => {
    return (
      <li>
        {body}—{author}
      </li>
    );
  };
  return <ul> {props.comments.map(renderComment)} </ul>;
};

Closing Components without children

When closing components without children, use self-closing tags:

return ( 
    <Foo> 
        <Bar /> 
    </Foo>     
);

List iterations

When rendering a list of components from an array, do it inline if it makes sense. If the map function is too long or complicated, consider extracting it out into its own method on the component

 const [fooList, setFooList] = useState(['orange', 'fish curry']);
  return (
    <ul>
      {fooList.map((fooItem) => (
        <FooItem>{fooItem}</FooItem>
      ))}
    </ul>
  );

Formatting Attributes

Maintain consistent formatting for attributes, especially for input elements:

 const [foo, setFoo] = useState("apple");
  const handleInputChange = (e) => {
    setFoo(e.target.value);
  };
  return (
    <input
      type="text"
      value={foo}
      onChange={(e)=>handleInputChange(e)}
    />
  );

Inline CSS styles

Set static CSS properties in SCSS and dynamic ones in JavaScript:

.Foo {
  background-color: #ff0;
}
import React from "react";

const Foo = () => {
  const [position, setPosition] = React.useState(0);
  const styles = { transform: "translateX(" + position + "px)" };

  return (
    <div className="Foo" style={styles}>
      Foo Header
    </div>
  );
};

export default Foo;

Use "classnames" to set CSS classes

Use the classnames node module for setting CSS classes on an element.

import React from 'react';
import classnames from 'classnames';
const Foo = () => {
  const classes = classnames('FooHeader', {
    'is-fixed': this.state.fixed,
    'is-visible': this.state.visible,
  });
  return <div className={classes}>Foo Header</div>;
};

export default Foo;

These best practices will help you maintain a clean, organized, and consistent codebase while working on React projects. Keep in mind that specific project requirements may lead to some adjustments in these guidelines.