Essential React Techniques for Top Development in 2023
Tags: react, webdev
Published: 2023-05-03 00:18:02

by [Siddhant Varma](https://blog.openreplay.com/authors/siddhant-varma)

<blockquote><em>According to [StackOverflow 2022 developer survey](https://survey.stackoverflow.co/2022/#most-loved-dreaded-and-wanted-webframe-want), [React](https://reactjs.org/) was the most wanted framework in 2022 -- the most popular framework for the fifth year in a row. React continues to progress rapidly, and adopting best practices is important to ensure the best outcomes. This article does explore some of these best practices to make the most out of React.</em></blockquote>


First, let's understand what makes React a tricky library to work with.

React is a minimal view library that differs from full-blown frameworks like Angular.  Where Angular, by default, comes equipped with various tooling, architectural patterns, and libraries, React heavily relies on the open-source community for even the most common libraries. 

Regarding architecture and following conventions, React is highly flexible and unopinionated. This can leave developers with decision fatigue when structuring their projects, naming files and folders, and optimizing performance. 

Without clear guidelines, developers may inadvertently write anti-patterns in their React applications, making them unnecessarily complex, difficult to scale, and low-performing. To avoid these anti-patterns and create a more robust, scalable, and high-performing React application, let's explore some common techniques that can be implemented.

## Folder Structure and Naming Conventions


When starting a new React project, I follow a standard directory structure and naming conventions for files and folders. 

This saves time later on when the project becomes complex and helps with better architecture. The folders are named in lowercase, with the separation of concerns principle in mind. Pages are grouped in a `pages` directory, with each page's UI broken down into components. These components are further broken down based on their granularity. I'm using [Atomic Design](https://dev.to/sanfra1407/how-to-organize-your-components-using-the-atomic-design-dj3#atomic-design) to organize UI components into atoms, molecules, and organisms. 

For instance, a Signup page can be composed of a Signup Form and a Signup Button, with the Signup Form comprising of a Form component, which in turn can have input and label components. Additionally, logic can be organized into utility functions, custom hooks, higher-order components, etc. Each component is also placed in its folder.

Based on the information above, the following is an example of the directory structure that a React project could have:

```shell
.
└── src/
    ├── components/
    │   ├── atoms/
    │   │   ├── button/
    │   │   │   ├── Button.js
    │   │   │   └── Button.css
    │   │   ├── input
    │   │   └── ...
    │   ├── molecules/
    │   │   ├── form/
    │   │   │   ├── Form.js
    │   │   │   └── Form.css
    │   │   └── ...
    │   └── organisms/
    │       ├── forms/
    │       │   ├── signup-form/
    │       │   │   ├── SignupForm.js
    │       │   │   └── SignupForm.css
    │       │   └── ...
    │       └── ...
    ├── state/
    │   ├── actions
    │   └── reducers
    ├── hoc 
    ├── pages/
    │   └── Signup/
    │       ├── Signup.js
    │       └── Signup.css
    ├── api/
    │   ├── constants
    │   └── functions/
    │       ├── auth.js
    │       ├── user.js
    │       └── ...
    ├── hooks/
    │   ├── useAuth.js
    │   ├── useLocalStorage.js
    │   ├── useWindow.js
    │   └── ...
    └── utils/
        ├── dates.js
        ├── formatString.js
        └── ...
```

If you use a state management library like [Redux](https://redux.js.org/) or React's default [Context API](https://reactjs.org/docs/context.html), you can organize your actions and reducers into separate folders under the `state` directory as shown above. 

All utility functions and custom hooks are camel-cased. React by default endorses using the “use-” prefix when naming custom hooks. The components folder is also broken down into atoms, molecules, and organisms based on the granularity of the components you build in your React application. 

## Fragments

[Fragments](https://reactjs.org/docs/fragments.html#gatsby-focus-wrapper) help you avoid unnecessary HTML in your React components. Let's walk through what they are and how we use them. 

When creating a new React component, you'll likely start by returning an empty `<div>` element, since a React component can only return a single root element. 
    
```jsx
export default function MyComponent() {
  return (
    <div>
      <h1>This is my component</h1>
    </div>
  );
}
```


However, this approach can result in a lot of unnecessary `<div>` elements in your code, bloating your templates.

Fortunately, React provides a solution to this problem through Fragments. Fragments allow you to use void HTML elements as wrappers without adding unnecessary elements to your code. 

```jsx
import React from "react";

export default function MyComponent() {
  return (
    <React.Fragment>
      <h1>This is my component</h1>
    </React.Fragment>
  );
}
```

Further, you can also directly use an empty HTML element (`<>`) as a shorthand notation for a fragment:

```jsx
import React from "react";

export default function MyComponent() {
  return (
    <>
      <h1>This is my component</h1>
    </>
  );
}
```

## Composability and Reusability 

The next best practice you can adopt when building your React components is making them composable and reusable. Let's understand how. 
    
In the past, I used to create my React projects with a single, large component because I found it too time-consuming to break things down into smaller components. However, I soon realized that as the app grew in complexity, it became even more challenging to refactor and test the code. Additionally, my development speed slowed down as I had to write all components from scratch without reusing them.

Take a look at the following `HomePage` component:


```jsx
import React from "react";
export default function HomePage() {
  return (
    <>
      <div>
        <div>
          <svg className="gradient">
            <path
              fill="url(#45de2b6b-92d5-4d68-a6a0-9b9b2abad533)"
              fillOpacity=".3"
              d="M317.219 518.975L203.852 678 0 438.341l317.219 80.634 204.172-286.402c1.307 132.337 45.083 346.658 209.733 145.248C936.936 126.058 882.053-94.234 1031.02 41.331c119.18 108.451 130.68 295.337 121.53 375.223L855 299l21.173 362.054-558.954-142.079z"
            />
            <defs>
              <linearGradient
                id="45de2b6b-92d5-4d68-a6a0-9b9b2abad533"
                x1="1155.49"
                x2="-78.208"
                y1=".177"
                y2="474.645"
                gradientUnits="userSpaceOnUse"
              >
                <stop stopColor="#9089FC" />
                <stop offset={1} stopColor="#FF80B5" />
              </linearGradient>
            </defs>
          </svg>
        </div>
        <nav className="navbar">
          <div>
            <a href="#">
              <span>Home</span>
            </a>
          </div>
          <div>
            <button type="button">
              <span>Open Menu</span>
            </button>
          </div>
          <div className="hidden lg:flex lg:gap-x-12">
            <a href="#">Page 1</a>
            <a href="#">Page 2</a>
            <a href="#">Page 3</a>
            <a href="#">Page 4</a>
          </div>
          <div>
            <a href="#">
              Log in <span>→</span>
            </a>
          </div>
        </nav>
        <main>
          <div>
            <div>
              <div class="banner">
                <div>
                  <p>This is an announcement!</p>
                </div>
              </div>
              <div className="hero">
                <h1>Get onboarded today</h1>
                <p>lorem ipsum dolor sit amet, consectetur adip</p>
                <div className="mt-10 flex items-center justify-center gap-x-6">
                  <a href="#">
                    Learn more <span>→</span>
                  </a>
                </div>
              </div>
            </div>
          </div>
        </main>
      </div>
    </>
  );
}
```

As you add more HTML to the HomePage component, it becomes longer and more difficult to refactor, test, and debug. To address this issue, it is better to break down the `HomePage` component into smaller, composable components:
    
```jsx
import React from "react";
import { Gradient } from "./Gradient";
import { Navbar } from "./Navbar";
import { Hero } from "./Hero";

export default function HomePage() {
  return (
    <>
      <Gradient />
      <Navbar />
      <Hero />
    </>
  );
}
```


This not only makes the code more readable and maintainable but also enables us to build future features of our React application faster. For instance, we can reuse the Navbar component in other parts of the app by passing some props to it.

<div style="background-color:#efefef; border-radius:8px; padding:10px; display:block;">
<hr/>
<h3><em>Session Replay for Developers</em></h3> 
<p><em>Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data.</em></p>
<img alt="OpenReplay" style="margin-top:5px; margin-bottom:5px;" width="768" height="400" src="https://raw.githubusercontent.com/openreplay/openreplay/main/static/openreplay-git-hero.svg" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<p><em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em><p>
<hr/>
</div>


## Performance 

Why bother building a React application if it doesn't perform well? As your app grows in complexity, it becomes increasingly important to focus on its performance. Performance is influenced by multiple factors, so we'll explore some key factors that impact the performance of your React application, provide examples, and suggest ways to improve it.

### Avoid Deeply Nested Components

We’ve seen that when working with larger and more complex components, you need to break them down into smaller components. However, this might develop into a performance anti-pattern where you start nesting too many smaller components inside one big single component. 

For instance, let’s look at the following `Counter` parent component. It renders two separate components called `Increment` and `Decrement`:

```jsx
import { useState } from "react";

export default function Counter() {
  const [count, setCount] = useState(0);
  const Increment = () => {
    return <button onClick={handleIncrement}>Increment +</button>;
  };
  const Decrement = () => {
    return <button onClick={handleDecrement}>Decrement -</button>;
  };
  const handleIncrement = () => setCount(count + 1);
  const handleDecrement = () => setCount(count - 1);

  return (
    <div>
      <Increment />
      Value of Count: {count}
      <Decrement />
    </div>
  );
}
```

Both `Increment` and `Decrement` are children component that lives inside the parent `Counter` component and use functions defined in the parent. By defining it here, we don't need to provide a prop for that function. It feels very intuitive, and it works perfectly fine. But there's a major problem here. Every time the parent component is rendered it will also redefine the child component, which means it gets a new memory address, and that could lead to performance issues and unpredictable behavior.

The solution is to either not define the children component at all or to move them out of the parent and pass the function in as a prop, as shown below:

```jsx
import { useState } from "react";

const UpdateCount = ({ handleClick, type }) => {
  return (
    <button onClick={handleClick}>
      {type === "increment" ? <>Increment +</> : <>Decrement -</>}
    </button>
  );
};

export default function Counter() {
  const [count, setCount] = useState(0);

  const handleIncrement = () => setCount(count + 1);
  const handleDecrement = () => setCount(count - 1);

  return (
    <div>
      <UpdateCount type={"increment"} handleClick={handleIncrement} />
      Value of Count: {count}
      <UpdateCount type={"decrement"} handleClick={handleDecrement} />
    </div>
  );
}
```
This example shows how React may seem simple at first, but if you lack a complete understanding of its inner workings, it's possible to inadvertently cause problems for yourself.

### Understand useMemo and useCallback hooks

The [`useMemo`](https://reactjs.org/docs/hooks-reference.html#usememo) and [`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback) hooks are the most important hooks used for performance consideration. However, we must understand where and when they should be used. 

It's natural to have more than one state in a component and perform computations when those state value change. Consider the following example where we have two pieces of state, `regularState` and `expensiveState`:

```jsx
import { useState } from "react";

export default function HeavyComponent() {
  const [regularState, setRegularState] = useState();
  const [expensiveState, setExpensiveState] = useState();

  const expensiveComputation = () => {
    console.log(
      "This expensive computation will run everytime expensiveState changes"
    );
  };

  const handleExpensiveStateClick = () => {};

  const handleRegularStateClick = () => {};

  const expensiveComputationResult = expensiveComputation();

  return (
    <>
      <button onClick={handleRegularStateClick}>Mutate regular state</button>
      <button onClick={handleExpensiveStateClick}>
        Mutate expensive state
      </button>
    </>
  );
}
```
Let's say we also have a function as in the above snippet, called `expensiveComputation`, that performs an expensive calculation whenever the state value of `expensiveState` changes. However, with the way the above code is written, the `expensiveComputation` function will run every time the component re-renders, and we know that all state changes lead to re-renders of a component. Thus even when you mutate `regularState`, the `expensiveComputation` function will run. The more pieces of state you have in this component, the more often that function will run, leading to performance issues for your component `HeavyComponent`. 

To fix this performance bottleneck, you can use the `useMemo` hook. 

```jsx
const expensiveComputationResult = useMemo(
  expensiveComputation(expensiveState),
  [expensiveState]
);
```
When we wrap the function execution in a `useMemo` hook, the function is invoked only when the value of `expensiveState` changes. 

The `useMemo` equivalent for dealing with components re-rendering when a prop value changes is the `useCallback` hook. When you wrap a function that mutates the prop of a child component inside a `useCallback` hook, the component only re-renders when its underlying prop changes.

### Optimize Load Time with Lazy Loading

A common performance nuance you might run into as your app grows big in size and complexity is a bigger bundle size that takes a long time to load. Lazy loading can help you achieve that; let's see how. 

Both [Next.js](https://nextjs.org/) and [Create-React-app](https://create-react-app.dev/) come with default support for code splitting. Code splitting is a technique that allows you to asynchronously import a certain piece of code when it's required. 

For instance, you can asynchronously lazy load assets like images, css, etc in your React components:

```jsx
export default function AsyncImport() {
  const greeting = async () => {
    const { greetingImage } = await import("./greeting.png");
    return greetingImage;
  };
  return <>...</>;
}
```

So now only when the `greeting` method is invoked the `greetingIamge` will be loaded in your application. 

React also allows you to lazy load components besides just JavaScript files:

```jsx
import { Suspense, lazy } from "react";

const LazyLoadedComponent = lazy("./LazyLoadedComponent");

export default function AsyncImport() {
  const greeting = async () => {
    const { greetingImage } = await import("./greeting.png");
    return greetingImage;
  };
  return (
    <>
      <Suspense fallback={<p>Loading...</p>}>
        <LazyLoadedComponent />
      </Suspense>
    </>
  );
}
```

You can lazy load the component import and wrap it in a special `Suspense` component which will then only import that React component in your application when needed. 

### Efficient State Management

Managing state in React applications is often a mess, especially for large applications that change quite frequently. Most of the performance bottlenecks in your app will be centered around mismanaged state causing unnecessary re-renders. 

You should try to use a good state management library like [Redux](https://redux.js.org/) or [React Query](https://react-query-v3.tanstack.com/) which helps you manage global state.

## Conclusion

Let's sum up what we've learned today. When beginning a new React project, it's recommended to adopt a modular directory structure and adhere to standard naming conventions. To enhance performance, try to avoid using unnecessary divs and consider using fragments instead. Additionally, aim to break down large components into smaller, reusable ones and keep performance considerations in mind while developing your React components.