paint-brush
Top 9 Tips to Improve React Performanceby@wownetort
10,868 reads
10,868 reads

Top 9 Tips to Improve React Performance

by Nikita StarichenkoSeptember 3rd, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

In React, function components and `React.PureComponent` provide two different ways of optimizing React apps at the component level. Function components prevent constructing class instances while reducing the overall bundle size as it minifies better than classes. Memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again. Lazy Loading React Components is a new function that allows you to load react components lazily through code without help from libraries.

Company Mentioned

Mention Thumbnail
featured image - Top 9 Tips to Improve React Performance
Nikita Starichenko HackerNoon profile picture

Hi everyone! There is a lot of information about the various features of React. About various life hacks and best practices in this framework. I want to talk about how to write code more efficiently and use React to the maximum.

1. Function/Stateless Components and React.PureComponent

In React, function components and PureComponent provide two different ways of optimizing React apps at the component level.


Function components prevent constructing class instances while reducing the overall bundle size as it minifies better than classes. On the other hand, in order to optimize UI updates, we can consider converting function components to a PureComponent class (or a class with a custom shouldComponentUpdate method).


However, if the component doesn’t use state and other life cycle methods, the initial render time is a bit more complicated when compared to function components with potentially faster updates.


React.PureComponent does a shallow comparison on state change. This means it compares values when looking at primitive data types, and compares references for objects. Due to this, we must make sure two criteria are met when using React.PureComponent:

  • Component State/Props is an immutable object;
  • State/Props should not have a multi-level nested object.

All child components of React.PureComponent should also be a Pure or functional component.


2. Memoize React Components

Memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again. A memoized function is usually faster because if the function is called with the same values as the previous one then instead of executing function logic it would fetch the result from cache.


Let's consider below simple stateless UserDetails React component.

const UserDetails = ({user, onEdit}) => {
    const {title, full_name, profile_img} = user;

    return (
        <div className="user-detail-wrapper">
            <img src={profile_img} />
            <h4>{full_name}</h4>
            <p>{title}</p>
        </div>
    )
}


Here, all the children in UserDetails are based on props. This stateless component will re-render whenever props changes. If the UserDetails component attribute is less likely to change, then it's a good candidate for using the memoize version of the component:


const UserDetails = ({user, onEdit}) => {
    const {title, full_name, profile_img} = user;

    return (
        <div className="user-detail-wrapper">
            <img src={profile_img} />
            <h4>{full_name}</h4>
            <p>{title}</p>
        </div>
    )
}

export default React.memo(UserDetails)


This method will do a shallow equal comparison of both props and context of the component based on strict equality.


3. Lazy Loading React Components

React just added a new function called react.lazy(). It is a new function that allows you to load react components lazily through code splitting without help from any additional libraries. Lazy loading is the solution of rendering only needed or critical user interface items first, then quietly unrolling the non-critical items later. There is also a built-in component available called Suspense that is required by the lazy function. It’s used for wrapping lazy components. It takes a fallback property that accepts the react elements you want to render as the lazy component is being loaded. Great place to implement a spinner or loading message.


Here is a basic example of how it will look like:

const Tasks = lazy(() => import('./Tasks'))
class App extends React.Component {
    render() {
        return (
            <div className="App">
                <Suspense fallback={<h1>Loading…</h1>}>
                    <Tasks />
                </Suspense>
            </div>
        );
    }
}


4. Avoid using Index as Key for map

Using the key as the index can show your app incorrect data as it is being used to identify DOM elements. When you push or remove an item from the list, if the key is the same as before, React assumes that the DOM element represents the same component.


It's always advisable to use a unique property as a key, or if your data doesn't have any unique attributes, then you can think of using the shortid module which generates a unique key.

import shortid from "shortid";
{
    comments.map((comment, index) => {
        <Comment 
            {..comment}
            key={shortid.generate()} />
    })
}

If the data has a unique property, such as an ID, then it's better to use that property.


5. Avoid Inline Function Definition in the Render Function.

Since functions are objects in JavaScript ({} !== {}), the inline function will always fail the prop diff when React does a diff check. Also, an arrow function will create a new instance of the function on each render if it's used in a JSX property.


This might create a lot of work for the garbage collector.

default class CommentList extends React.Component {
    state = {
        comments: [],
        selectedCommentId: null
    }

    render() {
        const { comments } = this.state
        return (
           comments.map((comment)=> {
               return <Comment onClick={(e)=> {
                    this.setState({selectedCommentId:comment.commentId})
               }} comment={comment} key={comment.id}/>
           }) 
        )
    }
}


Instead of defining the inline function for props, you can define the arrow function.

default class CommentList extends React.Component {
    state = {
        comments: [],
        selectedCommentId: null
    }

    onCommentClick = (commentId)=> {
        this.setState({selectedCommentId:commentId})
    }

    render() {
        const { comments } = this.state;
        return (
           comments.map((comment)=> {
               return <Comment onClick={this.onCommentClick} 
                comment={comment} key={comment.id}/>
           }) 
        )
    }
}


6. Spreading props on DOM elements

You should avoid spreading properties into a DOM element as it adds unknown HTML attribute, which is unnecessary and a bad practice.

const CommentsText = props => {
    return (
      <div {...props}>
        {props.text}
      </div>
    )
  }


Instead of spreading props, you can set specific attributes:

const CommentsText = props => {
    return (
      <div specificAttr={props.specificAttr}>
        {props.text}
      </div>
    )
}


7. Avoid Object Literals

This performance tip is similar to the previous section about anonymous functions. Object literals don’t have a persistent memory space, so your component will need to allocate a new location in memory whenever the component re-renders:

function ComponentA() {
    return (
        <div>
            HELLO WORLD
            <ComponentB style={{/*  */}}
                color: 'blue',
            background: 'gold'
        }}/>
        </div>
    );
}

function ComponentB(props) {
    return (
        <div style={this.props.style}>
            TOP OF THE MORNING TO YA
        </div>
    )
}

Each time ComponentA is re-rendered a new object literal has to be “created” in-memory.


Additionally, this also means that ComponentB is actually receiving a different style object. Using memo and PureComponent won’t even prevent re-renders here This tip doesn’t apply to style props only, but it’s typically where object literals are unwittingly used in React components.


This can be easily fixed by naming the object (outside of the component’s body):

const myStyle = {  // 
    color: 'blue',
    background: 'gold'
};
function ComponentA() {
    return (
        <div>
            HELLO WORLD
            <ComponentB style={myStyle} />
        </div>
    );
}

function ComponentB(props) {
    return (
        <div style={this.props.style}>
            TOP OF THE MORNING TO YA
        </div>
    )
}


8. Virtualize a Large List Using react-window

When you want to render an enormous table or list of data, it can significantly slow down your app’s performance. Virtualization can help in a scenario like this with the help of a library like react-window. react-window helps solve this problem by rendering only the items in the list that are currently visible, which allows for efficiently rendering lists of any size.

import { FixedSizeList as List } from 'react-window'

const Row = ({ index, style }) => (
    <div className={index % 2 ? 'ListItemOdd' : 'ListItemEven'} style={style}>
        Row {index}
    </div>
)
const Example = () => (
    <List
        className="List"
        height={150}
        itemCount={1000}
        itemSize={35}
        width={300}
    >
        {Row}
    </List>
)


9. Use React.Fragments to Avoid Additional HTML Element Wrappers

React.fragments lets you group a list of children without adding an extra node.

class Comments extends React.PureComponent {
    render() {
        return (
            <React.Fragment>
                <h1>Comment Title</h1>
                <p>comments</p>
                <p>comment time</p>
            </React.Fragment>
        )
    } 
}


Or you can do it like this:

class Comments extends React.PureComponent {
    render() {
        return (
            <>
                <h1>Comment Title</h1>
                <p>comments</p>
                <p>comment time</p>
            </>
        )
    } 
}



P.S. Thanks for reading!

More articles about frontend development:

  1. Some HTML, CSS Little Secrets In One Article
  2. Top Lesser Known HTML 5 & CSS 3 Tips and Best Practices
  3. Top 12 Lesser Known Tips for JavaScript Best Practices