DEV Community

Cover image for My experience after 5 years of developing React apps
tonymtz
tonymtz

Posted on

My experience after 5 years of developing React apps

Welcome to my post. Here I'm talking about lessons learned after developing React apps for more than 5 years now.

The points I am about to present are a good starting point to tune up your React code. It is important to be critical of your past self and to learn from mistakes. Falling into errors is not a bad thing; a bad thing is not learning from failures.

Class-based vs Functional Components

Classes in javascript are not a native thing yet. To the class syntax, we require a transpiler to translate ES7 into plain old-fashioned ES5. The problem with classes is the footprint they have in the final file size to support all the syntactic sugar added. In addition, Dan Abramov has a great blog where he mentions other issues related to the use of JS classes.

// class-based component
class Text extends React.Component {
  render() {
    return <span>{this.props.children}</span>
  }
}

// functional component
const Text = props => {
  return <span>{props.children}</span>;
};

HOCS vs. Hooks

This is somehow related to the previous point. People used to point out that functional components were hard to develop because of the lack of control of the component’s life cycle. And it was true until not much ago.

React v16.8 introduced the Hooks API, which allows us to have more control over functional components. Unlike lifecycle methods, hooks are less explicit but more flexible. Certainly, Hooks API brings a different set of trade-offs compared to HOCs. I.e, from react-redux the useSelector and useDispatch hooks are way easier to write than connect, but calling those hooks makes the component more tied to Redux instead of separating it.

Hooks are here to stay and widens the limits of functional components. For more information, you can check this page: https://wattenberger.com/blog/react-hooks.

Component Composition? Inheritance?

The preferred way to create new components from others (reuse code between components) should be through composition.

React provides a powerful composition model. Using Props correctly allows us enough flexibility for any use case. I haven’t found any situation where inheritance is the only way. On the other hand, a huge caveat I’ve seen in the past using inheritance is that overriding behavior is logically a pain to read.

But please, be mindful. We don’t want to navigate thought a thousand components with small tweaks every time. Keep the creation of new components to the minimum required.

// Ugly way

const Text = () => {
  // ... some implementation ...
}

class H1 extends Text {
  // ... override some implementation ...
}

class H2 extends H1 {
  // ... override some implementation ...
}

// Best way

const Text = () => {
  // ... some implementation ...
}

const H1 = props => <Text tag="h1" {...props} />;

const H2 = props => <Text tag="h2" {...props} />;

How to manage states?

Fortunately, we don’t lack alternatives for managing app states. Perhaps, this is a double-edged knife. We can easily fall into a wrong solution that might work in the first instance, just to figure that we need to rewrite a critical part of our app, and if unfortunate enough, a few components too.

This is a quick useful reference guide I found on Twitter.

states

CSS in Javascript? Yes.

It is not a bad idea. Let me explain why and if you disagree, let me know in the comments section!

CSS-in-JS provides some advantages like no risk of collisions and reduced file size but with limited distribution, styling can’t be easily shared with other projects.

In the other hand, separated CSS provides more code clarity using naming conventions like BEM, it’s framework agnostic meaning it can be distributed independently (your CSS will work fine on either React, Angular, jQuery or Vanilla), but this leads to having more code, unused remainders or complex bloated styles.

CSS-in-JS might be suitable for your project, or might be not. There’s not a final answer on this, and probably people will read this and say: “it’s bullshit”. As most of the patterns, use whatever works better and makes your life easier!

Write tests for all code

You can easily divide tests into two big parts: testing the functionality of components as individuals, and tests on the app as a whole once it renders in the browser.

It’s incredible the number of courses available on the internet that does not even mention this topic. Testing your app is really important.

Follow linter rules

A linter is a tool that analyzes source code to flag stylistic errors and suspicious constructs, among other capabilities. Using a linter in your code helps to keep your code ready to read and typos free (mostly).

Use the Production Build for Production Environment

This depends on how you are building your app of course. It might sound funny but, lots of people benchmark their apps with development builds. The results will vary dramatically since dev builds are usually not minified or optimized.

Slow behavior? Performance Tab is your best friend

At least in Chromium-based browsers, the Performance tab provides a bunch of useful tools for profiling web applications. The reading is really simple.

Another important factor that negatively affects the readings is the chrome extensions you have enabled. Disable all chrome extensions, temporarily at least. I’ve suffered in the past because the adblocker was blocking important requests or heavy-content-analyzers runs after the page loads.

Keep in mind that all numbers are relative.

Virtualize Long Lists

When rendering an important number of list elements (hundreds or thousands rows), you can render a small number of items at any given time to reduce the number of DOM nodes created.

react-virtualized is a popular library for this. Few websites that use this technique (not this lib exactly) are Twitter and Imgur.

Use Immutable Data Structures

When a component’s prop or state changes, React compares the newly returned element with the previously rendered one to decide if whether or not it should recreate the DOM node. This process is called reconciliation.

A simple way to avoid reconciliation is to avoid mutating values that you are using as props or state. There are few alternatives to achieve this, for example: using concat instead of push for arrays; using Object.assign or spread syntax; or libraries like ImmutableJS.

Summary

I wanted to mix a few basic topics with a few advanced ones, just to give a general idea. I am confident that if you find any topic interesting, you will google it for more details. The key to fine-tuning your app is to use the minimum needed to have your business logic working and avoid bloating your app with dependencies you might need or might not need in the future.

Top comments (0)