Writing cleaner React code
Tags: react, javascript, codequality, typescript
Published: 2022-04-13 21:26:18

This article is not about low-level guidelines on writing clean code. This article focuses on high-level concepts that are not addressed or often talked about.

We often hear about writing clean code, when it comes to React we are told to write smaller components or break large components into smaller ones. While this is true this certainly doesn't clarify how you can write cleaner components.

Here is a small set of slightly opinionated rules you can follow while writing a react component.

## Enforce a consistent structure while writing components.

Enforcing a simple structure to your components makes it easier to navigate and read your code. Although this looks simpler, it can be difficult to get used to it at first. To hack this start writing comments for each section, once you are comfortable enough you'll start to see those comments fade away while maintaining the structure.

Here's a simple example of how you can write a component by following a structure.

```typescript
// Sort imports alphabetically

// Package Imports
import merge from 'lodash/merge'
import React from "react";
// Package Type Imports
import type { Resolvers, TypePolicies } from '@apollo/client'
import type { useState, useEffect } from "react";

// App Imports
import BarChart from 'components/charts/BarChart'
import Block from 'components/blocks/Block'
import Card from 'components/card/Card'
import Loader from 'components/loaders/Loader'
// App Type Imports
import type { BlockProps } from 'components/blocks/Block'

// Component types
type ComponentProps {
 ...
}

// Styled components
const StyledText = styled('p', {
 ...
}

// Constants or variables that's has no effect on rerender
const URL = "https://www.google.com/";

const CleanComponent = (props) => {
  // Store/State/Callbacks/Refs/Context/Memo
  const [open, setOpen] = useState(false);

  // UI State
  const [isLoading, setIsLoading] = useState(props.isLoading);

  // Events
  const handleAction = () => {
    setOpen(true)
  }

  // Networking
  const fetchDataFromAPI = () => {
    fetch(URL).then(() => {
      // do something
    })
  }

  // Effects
  useEffect(() => {
    // do something	
  }, [])

  // JSX
  return (
    <div>
      <p>Writing clean react code</p>
    </div>
  )
}

// Type exports
export type { ComponentProps }

export default CleanComponent
```

## Component Abstraction
While this is the most powerful concept it is highly misunderstood. Deciding when to break a component into smaller ones or not is something that comes with a lot of practice, but there are some rules you can follow when in doubt.

## Components that are at same level of abstraction should be together
When writing components you often get a feeling of breaking it into smaller components, but it's not really a sub-component. When components are on the same level of abstraction just keep them in the same file or create a components folder for that component.

```jsx
const Menu = () => (
  <i className="fa fa-menu" />
)

const Navbar = () => (
  <div>
    <Menu />
    <h1>App</h1>
  </div>
)
```

## Deciding when to break a component into smaller ones

This is non-trivial, especially when you are working on a complex component. To solve this, make a set of rules that you follow when your component becomes too complex.

- Follow the Single Responsibility Principle.
- Reusability is another important criteria for breaking components. If you see yourself writing the same component, again and again, try abstracting it out
- When you see a clear abstraction i.e. when a small part of your complex component can be extracted out and live in its own world (this includes everything like local state, network call, event handling, etc).
- When component props goes higher that some number x. This is an indication that your component is becoming complex and needed to be broken into smaller components