Performance Tips in React.js

Performance Tips in React.js

        Haz clic para leer esta publicación en español.

Applying this technique can lead to a significant performance boost.

Code splitting

Implementing this technique can yield a substantial performance boost. The concept behind code splitting is to lazy load the code only when it's needed. For example, if a module in our project uses a large PDF rendering library, there's no need to render any PDFs during login, so it's much better not to load the pdfjs library. That's the essence of this technique, which is based on JavaScript standards and has syntax like this:


import('/module.js').then( module => { // do something with the module}, error => { // an error occurred while exporting the module})

We can bring this to React using the React.lazy library, which allows us to load components on-demand. Here's the trick:


const Mycomponent = React.lazy(() => import('../mymodule'))

In the JSX part of the component, we'll need to use suspense, otherwise, React will try to render our component even if it hasn't been completely loaded. React.suspense delays the rendering of our component until a condition is met and shows a fallback state (this fallback can be text, a div, or another component).


javascript

Loading...

}>

This way, our component initially shows a div with the text "Loading...", and once the library is fully loaded, it displays MyComponent. We can creatively add conditionals to show certain components, like a checkbox if we want MyComponent to be loaded or whatever else; the sky's the limit.

Eager Loading

This technique allows us to load the library "hurriedly," i.e., before the user needs it. If we somehow detect that the user intends to use a module or meets the condition to load the library, then we can preload it so the user doesn't have to wait (we never want the user to wait). For example:


// declare a function that imports the library
const loadMyComponent = () => import('../mymodule')
const Mycomponent = React.lazy(loadMyComponent)

In our JSX, we can trigger our loadMyComponent function when the user hovers over a button, for example:

Loading...

}>

The trick behind this technique is that browsers have a cache, and no matter how many times we import a library, it will only be downloaded once. Eager loading is a tool, and as such, we can use it creatively. Only imagination is the limit.

Webpack Magic Comments

If you're using webpack to build your application, you can use webpack magic comments to let webpack add prefetch instructions to the browser. You can also do things like name chunks or select different modes.


import(/* webpackPrefetch: true */ "./mymodule.js")

This will result in something like this in the HTML head, and it will be loaded during idle time. You can consult the webpack documentation on prefetch.

Finally, I'd like to make a note about using React.suspense. We often tend to nest the entire application or many components within suspense, but this isn't always recommended because we'd be waiting for suspense to finish loading before we can actually use the UI. In the meantime, the browser will show the fallback we've configured, which translates to waiting for the user (we never want the user to wait). The same goes for the opposite case, wrapping each component we want to load deferentially in suspense (it would be a nightmare to maintain). My recommendation is that for the code to be maintainable and work smoothly without crazy waits, we should consider who shares common conditions so we can nest within suspense and show directly what we need the user to see first. In the end, it all depends on the application flow.

UseMemo

This hook is a great help when we have a large list or a value that is expensive to calculate within our component. If, for example, we have a state variable and an independent area calculation, in that case, we want to preserve the result of the heavy calculation so we don't have to repeat the execution (the result won't change).


function MyComponent(x,y){
const area = calculateArea(x, y)
return(

The area is {area}

)
}

If the parent component of MyComponent is re-rendered or if we add an independent state, then the area will be recalculated. To avoid this, we'll use the React.useMemo hook that accepts 2 parameters, the function to create and an array of dependencies, resulting in something like this:


function MyComponent(x,y){
const area = React.useMemo(() => calculateArea(x, y), [x,y])
return(

The area is {area}

)
}

Now the area calculation will only be done if the dependent variables change.

Workers

Since IE10, workers have been supported in browsers, allowing scripts to be executed in the background. JavaScript by default runs only on one thread, using workers we can run costly tasks on other threads and free up execution to continue its course. I'm not going to delve into this concept, but know that there's a Workerize loader that can make life much easier for loading workerized modules.

Memo

Most interactions we'll have with our application will trigger a re-render, but not all components need to be rendered, if nothing changed for them it wouldn't be ideal for them to be spending resources redrawing. To solve this situation, we can use React.memo() which allows us to maintain a memoized version of the wrapped component.


function MyComponent(){ return(

Hello, I'm a component

) }
const myComponent = React.memo(MyComponent);

We can also pass it a function to manually compare previous and current states to decide what to do:

function MyComponent(){ return(

Hello, I'm a component

) }
const myComponent = React.memo(MyComponent, (prevProps, nextProps) => {
// return true
});

We can return true if we don't want it to be redrawn for any reason, if it returns false it will redraw. This gives us the basic guideline of comparing states to know whether we want to redraw or not:


function MyComponent(){ return(

Hello, I'm a component

) }
const myComponent = React.memo(MyComponent, (prevProps, nextProps) => { if(prevProps.props !== nextProps.props) return false
});

As a final step, I would like to remind you that if you pass unchanged states to React.memo(MyComponent) when using it, it won't redraw by default. This can save us from writing many custom state comparators.

React-virtual performance monitoring...in progress