How to Maintain the Performance of a React Application on a Daily Basis
Tags: react, performance, css
Published: 2023-09-06 07:42:59

I'm a fast-paced person: I eat quickly, walk briskly, and make decisions in a snap. What I can't stand, though, is a slow app — especially one I'm working on. So, I always find myself compelled to scrutinise its performance: examining how many times it re-renders, the size of the DOM, and the speed of user interactions, among other things. Armed with this data, I incrementally improve the app's performance by refining the codebase in a targeted way.

Now, I'd like to share some strategies that could help enhance your app's performance:

- **Be Mindful of the Hooks You Use in Each Component**: From my perspective, this is crucial. `useState` and `useEffect` are the most commonly used hooks, but sometimes they can be eliminated with a bit of refactoring, without sacrificing functionality. Fewer hooks mean fewer re-renders, which in turn means a faster app. Other noteworthy hooks are `useCallback` and `useMemo`, which memoize values, storing them in memory. On a larger scale, this can have an impact on performance. The same goes for `React.memo()`.

_Simple example:_

Imagine you have a component that tracks a user's first name, last name, and age. A naive approach could be to declare three separate `useState` hooks:

```
const UserProfile = () => {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  const [age, setAge] = useState(0);
  
  // Multiple function calls to update the user profile
  const updateUserProfile = () => {
    setFirstName('John');
    setLastName('Doe');
    setAge(30);
  };
  
  return (
    <div>
      <h1>{`${firstName} ${lastName}`}</h1>
      <p>Age: {age}</p>
      <button onClick={updateUserProfile}>Update Profile</button>
    </div>
  );
};
```

Every time each of those hooks is called, the component will re-render, even if it happens simultaneously. We could condense this into a single `useState` by using an object:

```
const UserProfile = () => {
  const [user, setUser] = useState({
    firstName: '',
    lastName: '',
    age: 0,
  });

  // Single function call to update the user profile
  const updateUserProfile = () => {
    setUser({
      firstName: 'John',
      lastName: 'Doe',
      age: 30,
    });
  };

  return (
    <div>
      <h1>{`${user.firstName} ${user.lastName}`}</h1>
      <p>Age: {user.age}</p>
      <button onClick={updateUserProfile}>Update Profile</button>
    </div>
  );
};
```

Now, we have only one hook, and only one re-render when the hook is called. Bingo.

- **Minimise the Number of DOM Nodes**: I always ask myself, "Can this HTML tag be safely removed?" If the answer is yes, I remove it. Fewer nodes lead to better performance.

_Simple example:_

If you find the code using empty `div`s solely for styling purposes, consider using CSS Grid or Flexbox to achieve the same layout, thus reducing the number of DOM nodes.

```
// Before
<div className="wrapper">
  <div className="innerWrapper">
    <Component />
  </div>
</div>

// After
<div className="optimizedWrapper">
  <Component />
</div>
```

- **Favor Class-Based Styling Over Inline Styles**: Inline styles are considered new objects on each render, which can make React think something has changed, triggering unnecessary re-renders and affecting performance. It's also worth noting that maintaining styles in separate files helps keep the styling consistent.

_Simple example:_

```
// Before
<div style={{ display: 'flex' }}>

// After
<div className='container'>

// In a separate css file: 
.container: {
  display: flex
}
```

- **Prefer Simple `div`s to `Box` from Material UI or Similar UI Libraries:** Native HTML elements generally offer better performance because they don't carry the overhead of additional React components, including lifecycle methods and context consumption.

_Simple example:_

```
// Before
<Box p={2}>

// After
<div className="container">

// In a separate css file: 
.container: {
  padding: '2px'
}
```

- **Prefer Conditional Classes to Conditional Styles in `makeStyles` from Material UI or Similar UI Libraries:** Using predefined classes that are toggled based on conditions can be more performant, as it doesn't generate new classes at runtime. 

_Simple example:_

```
// Before
const useStyles = makeStyles({
  root: {
    backgroundColor: props => props.isActive ? 'blue' : 'white',
  },
});

// After
const className = isActive ? 'active' : 'inactive';
```
- **Opt for Functional Components:** There are several compelling reasons for this. First, functional components are easier for build tools like Webpack to optimize through "tree shaking," which only includes the code that's actually being used in the final bundle. Second, functional components often result in a smaller component tree and fewer lifecycle methods, allowing React's diffing algorithm to make quicker updates to the DOM. Lastly, functional components use less memory than class components, which is especially beneficial in environments where memory is limited.

_Simple example:_

```
// Before
class MyComponent extends React.Component {
  state = { /* ... */ };
  componentDidMount() { /* ... */ }
  render() { /* ... */ }
}

// After
const MyComponent = () => {
  const [state, setState] = useState(/* ... */);
  useEffect(() => { /* ... */ }, []);
  return (/* ... */);
};
```

By giving these tips a go and making them a regular part of your coding routine, you're not just on the path to a speedier React app, but you're also gonna make things a whole lot smoother for your users. 
Let's create better apps together =).