5 Tips to Improve the Performance of Your React Apps

栏目: IT技术 · 发布时间: 4年前

内容简介:Has your React app been feeling kinda sluggish? Are you afraid of turning on the “paint flash” in Chrome DevTools because of what you might see? Try out these 5 performance tips!This article contain 5 performance tips for React development. You can use thi

Has your React app been feeling kinda sluggish? Are you afraid of turning on the “paint flash” in Chrome DevTools because of what you might see? Try out these 5 performance tips!

This article contain 5 performance tips for React development. You can use this table of contents to navigate quickly around this article.

Jump to Tips

  • Use memo and PureComponent
  • Avoid Anonymous Functions
  • Avoid Object Literals
  • Use React.lazy and React.Suspense
  • Avoid Frequent Mounting/Unmounting

— Sorry to interrupt this program!

If you're interested in learning React in a comprehensive and structured way, I highly recommend you try Wes Bos' React for Beginners or Fullstack Advanced React & GraphQL course. Learning from a premium course like that is a serious investment in yourself.

Plus, these areaffiliate links, so if you purchase a course you help Alligator.io continue to exist at the same time! :pray:

- Seb, :v:+:heart:

Use memo and PureComponent

Consider this simplistic React app below. Do you think that <ComponentB> will re-render if only props.propA changes value?

import React from 'react';

const MyApp = (props) => {
  return (
    <div>
      <ComponentA propA={props.propA}/>
      <ComponentB propB={props.propB}/>
    </div>
  );
};

const ComponentA = (props) => {
  return <div>{props.propA}</div>
};

const ComponentB = (props) => {
  return <div>{props.propB}</div>
};

The answer is YES! This is because MyApp is actually re-evaluated (or re-rendered :smirk:) and <ComponentB> is in there. So even though its own props didn’t change, it’s parent component causes it to re-render.

This concept also applies to render methods for Class-based React components.

The authors of React perceived this wouldn’t always be the desired result, and there would be some easy performance gains by simply comparing old and new props before re-rendering … this is essentially whatReact.memo andReact.PureComponent are designed to do!

Let’s use memo with functional components, (we’ll look at Class-based components after):

import React, { memo } from 'react';

// :no_good:‍♀️
const ComponentB = (props) => {
  return <div>{props.propB}</div>
};

// :ok_woman:‍♂️
const ComponentB = memo((props) => {
  return <div>{props.propB}</div>
});

That’s it! You just need to wrap <ComponentB> with a memo() function. Now it will only re-render when propB actually changes value regardless of how many times its parent re-renders!

Let’s look at PureComponent . It’s essentially equivalent to memo , but it’s for class-based components.

import React, { Component, PureComponent } from 'react';

// :no_good:‍♀️
class ComponentB extends Component {
  render() {
    return <div>{this.props.propB}</div>
  }
}

// :ok_woman:‍♂️
class ComponentB extends PureComponent {
  render() {
    return <div>{this.props.propB}</div>
  }
}

These performance gains are almost too easy! You might wonder why React components don’t automatically include these internal safeguards against excessive re-rendering.

There’s actually a hidden cost with memo and PureComponent . Since these helpers compare old/new props, this can actually be its own performance bottlenecks. For example, if your props are very large, or you’re passing React components as props, the comparison of old/new props can be costly.

Silver bullets in the world of programming are rare! And memo/PureComponent aren’t an exception. You’ll definitely want to test drive them in a measured, thoughtful way. In some cases, they can surprise you how much computational savings they can yield.

For React Hooks, check outuseMemo as a similar way to prevent unnecessary computational work

Avoid Anonymous Functions

Functions that are inside the main body of a component are usually event handlers, or callbacks. In many cases you might be tempted to use anonymous functions for them:

import React from 'react';

function Foo() {
  return (
    <button onClick={() => console.log('boop')}> // :no_good:‍♀️
      BOOP
    </button>
  );
}

Since anonymous functions aren’t assigned an identifier (via const/let/var ), they aren’t persistent whenever this functional component inevitably gets rendered again. This causes JavaScript to allocate new memory each time this component is re-rendered instead of allocating a single piece of memory only once when using “named functions”:

import React, { useCallback } from 'react';

// Variation 1: naming and placing handler outside the component 
const handleClick = () => console.log('boop');
function Foo() {
  return (
    <button onClick={handleClick}>  // :ok_woman:‍♂️
      BOOP
    </button>
  );
}

// Variation 2: "useCallback"
function Foo() {
  const handleClick = useCallback(() => console.log('boop'), []);
  return (
    <button onClick={handleClick}>  // :ok_woman:‍♂️
      BOOP
    </button>
  );
}

useCallback is another way to avoid the pitfalls of anonymous functions, but it has similar tradeoffs that accompany React.memo that we covered earlier.

With class-based components, the solution is pretty easy and doesn’t really have any downsides. It’s the recommended way to define handlers in React:

import React from 'react';

class Foo extends Component {
  render() {
    return (
      <button onClick={() => console.log('boop')}>  {/* :no_good:‍♀️ */}
        BOOP
      </button>
    );
  }
}

class Foo extends Component {
  render() {
    return (
      <button onClick={this.handleClick}>  {/* :ok_woman:‍♂️ */}
        BOOP
      </button>
    );
  }
  handleClick = () => {  // this anonymous function is fine used like this
    console.log('boop');
  }
}

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={{  {/* :no_good:‍♀️ */}
        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 :sob:

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 of course!):

const myStyle = {  // :ok_woman:‍♂️
  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>
  )
}

Use React.lazy and React.Suspense

Part of making your React app fast can be accomplished via code-splitting. This feature was introduced to React v16 with React.lazy and React.Suspense .

If you aren’t aware, the concept of code-splitting is where your JavaScript client source (eg., your React app code) is broken into smaller chunks, and only loads these chunks in a lazy fashion. Without any code-splitting a single bundle could be very large:

- bundle.js (10MB!)

Using code-splitting, this could cause the initial network request for the bundle to be significantly smaller:

- bundle-1.js (5MB)
- bundle-2.js (3MB)
- bundle-3.js (2MB)

The initial network request will “only” need to download 5MB, and it can start showing something interesting to the end user. Imagine a blog website that only needs the header, and footer initially. Once that’s loaded it’ll begin to request the 2nd bundle that contains the actual blog posts. This is a just rudimentary example where code-splitting would be handy. :clap::clap::clap:

How code splitting this done in React?

If you’re using Create React App , it’s already configured for code-splitting, so you can simply use React.lazy and React.Suspense out of the gate! If you’re configuring webpack yourself it should look like this .

Here’s a simple example where lazy and Suspense is implemented:

import React, { lazy, Suspense } from 'react';
import Header from './Header';
import Footer from './Footer';
const BlogPosts = React.lazy(() => import('./BlogPosts'));

function MyBlog() {
  return (
    <div>
      <Header>
      <Suspense fallback={<div>Loading...</div>}>
        <BlogPosts />
      </Suspense>
      <Footer>
    </div>
  );
}

Notice the fallback prop. This will be shown to the user immediately while the 2nd bundle chunk is loaded (eg., <BlogPosts> ).

Check out this great article on Code Splitting with React Suspense :crocodile:

Avoid Frequent Mounting/Unmounting

Many times we’re used to making components disappear using a ternary statement (or something similar):

import React, { Component } from 'react';
import DropdownItems from './DropdownItems';

class Dropdown extends Component {
  state = {
    isOpen: false
  }
  render() {
    <a onClick={this.toggleDropdown}>
      Our Products
      {
        this.state.isOpen
          ? <DropdownItems>
          : null
      }
    </a>
  }
  toggleDropdown = () => {
    this.setState({isOpen: !this.state.isOpen})
  }
}

Since <DropdownItems> is removed from the DOM it can cause a repaint/reflow by the browser. These can be expensive, especially if it causes other HTML elements to shift around.

In order to mitigate this, it’s advisable to avoid completely unmounting components. Instead, you can use certain strategies like setting the CSS opacity to zero, or setting CSS visibility to “none”. This will keep the component in the DOM, while making it effectively disappear without incurring any performance costs.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

鼠标宣言

鼠标宣言

约翰·里德尔 / 倪萍、梅清豪 / 上海人民 / 2005-08-01 / 25.00

本书针对信息时代营销者不知该如何满足消费者的营销困境,提出了崭新的解决方案——以新技术为基础的群体筛选和推荐系统。随着信息管理软件和internet的高速发展,群体筛选技术下的推荐系统通过大量有关消费者偏好和购物记录的信息,以及对产品特征的准确把握,能够为消费者进行精确的推荐,提高了消费者的购物效率和准确度以及营销者的营销效率和竞争力。本书通过通俗而到位的讲解,向读者全面介绍了有关群体筛选技术的理......一起来看看 《鼠标宣言》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具