2
Speaking of which, I haven't updated the sharing for a long time, and I have been in the state of boiling frogs in warm water. Because of some reasons and the fact that this society is really too scrolled, I was forced to join the scrolling army and revisit a lot of knowledge. At the same time, I did discover a lot of my own shortcomings. I also want to take this opportunity to get everyone to curl up. After all The ability is oneself. link to original text

Let me say a few words

Regarding what I want to write today, most of you can actually learn from the official React Then why do I still want to write? React for almost three years, I have not read the official documents seriously. What I want to tell may be someone similar to me, and also add some my own understanding and opinions.

Ask you a few questions

Performance optimization is a problem, I really can’t escape it forever. It’s an interviewer who has to ask a few words, but to be honest React did a great job, or the projects I’ve done are too basic, and I haven’t encountered them. What performance issues have caused me for a long time to have no idea that React has many APIs related to performance optimization.

Let's first look at the code. I directly define multiple components in a file for everyone to watch. When the code is formally written, a file is a component.

import React from 'react';

class Test extends React.Component {
  componentDidUpdate() {
    console.log('Test componentDidUpdate');
  }

  render() {
    return <div></div>;
  }
}

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState((state) => ({
      count: state.count + 1,
    }));
  }

  handleTestClick() {}

  render() {
    return (
      <div>
        <div>{this.state.count}</div>
        <div onClick={this.handleClick}>click</div>
        <Test onClick={this.handleTestClick} />
      </div>
    );
  }
}

There is nothing to say about this code, every time you click click update state , I will ask a few questions now, you should think about it first~

  1. Every time I click click , will the Test component print Test componentDidUpdate ?
  2. If I Test components React.Component replaced React.PureComponent , the above results the same? If it is not the same, why?
  3. What if I modify this line of code from <Test onClick={this.handleTestClick} /> to <Test onClick={() => {}} /> ?

shouldComponentUpdate

If all the contents to be talking about this stuff, shouldComponentUpdate as React part of the life cycle, most React developers or at least heard of it, it simply returns a Boolean value in this function, React based on this boolean Value to determine whether the component needs to be re-rendered.

shouldComponentUpdate receives two parameters, one is the updated props and the other is the updated state . You can compare the two props and state to determine whether you need to re-render the component.

import React from 'react';

class Test extends React.Component {
  componentDidUpdate() {
    console.log('Test componentDidUpdate');
  }
    
  // 每次点击 click 都会打印 Test componentDidUpdate
  // 添加这个函数后当 count 没有变化时不会打印 Test componentDidUpdate
  shouldComponentUpdate(nextProps) {
    if (this.props.count === nextProps.count) {
      return false;
    }
    return true;
  }

  render() {
    return <div></div>;
  }
}

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState((state) => ({
      count: state.count,
    }));
  }

  render() {
    return (
      <div>
        <div>{this.state.count}</div>
        <div onClick={this.handleClick}>click</div>
        <Test count={this.state.count} />
      </div>
    );
  }
}

This code can also be regarded as a relatively intuitive explanation of shouldComponentUpdate , why do you want to do this? When there is only one Test component, it may not have much impact. If there are 1,000 or even 10,000 Test , every time click there will be 1,000 or 10,000 Test componentDidUpdate called, which is a bit exaggerated Up. So when you use loop rendering components, you must pay attention to this point, it may become the bottleneck of your application.

Now let's solve the first question, every time I click click , will the Test component print Test componentDidUpdate ?

Yes, every time you click click , the Test component will print Test componentDidUpdate , unless we define Test shouldComponentUpdate , and return false prevent it from re-rendering.

PureComponent

Regarding React , I believe everyone is not that unfamiliar. According to the official document, Component and PureComponent are very similar. The difference between the two is that PureComponent implements the shouldComponentUpdate function, which is why I said to shouldComponentUpdate .

import React from 'react';

class Test extends React.PureComponent {
  componentDidUpdate() {
    console.log('Test componentDidUpdate');
  }
    // 错误的用法
  shouldComponentUpdate(nextProps) {
    if (this.props.count === nextProps.count) {
      return false;
    }
    return true;
  }

  render() {
    return <div></div>;
  }
}

If you shouldComponentUpdate PureComponent again, you should get such a warning. The side also tells us that PureComponent has implemented the function shouldComponentUpdate

Test has a method called shouldComponentUpdate(). shouldComponentUpdate should not be used when extending React.PureComponent. Please extend React.Component if shouldComponentUpdate is used.

Official website document said PureComponent to shallow contrast props and state ways to achieve this function, which is relatively shallow, relatively shallow that what is it? It can be simply understood as a === b , there is still some talk about it, but it is not within the scope of this article. Give two examples, you can search and understand by yourself.

let a = 5;
let b = 5;
let c = {};
let d = {};
console.log(a === b); // true
console.log(c === d); // false

When looking at a problem caused by improper code, everyone must pay attention to the content of this part.

import React from 'react';

class Test extends React.PureComponent {
  // 根据从 App 中传来的 animal 渲染组件
  // 在 App 中每次点击添加新的动物后, 这里还是原来的 dog
  render() {
    return <div>Test: {this.props.animal.join(',')}</div>;
  }
}

export default class App extends React.Component {
  constructor(props) {
    super(props);
    // 默认为一只狗
    this.state = { animal: ['dog'] };
    this.handleClick = this.handleClick.bind(this);
  }

  // 每次点击把新的值添加进 animal 中
  // 此处有一个 Bug, 由于 animal.push 方法虽然更新了原来的数组
  // 但是他们还是一个数组(这个说法有些奇怪), 指针还是一样的
  // 可能需要读者自行搜索理解 JS 中基本类型和引用类型的存储方式
  // 所以当 Test 组件接收到新的 animal 时, 通过浅比较会发现它们其实是一样的
  // 也就意味着 Test 不会重新渲染
  handleClick(val) {
    const { animal } = this.state;
    animal.push(val)

    this.setState({
      animal,
    });
  }

  // 根据 state 中的 animal 渲染组件
  render() {
    return (
      <div>
        <div>App: {this.state.animal.join(',')}</div>
        <div onClick={() => this.handleClick('cat')}>click</div>
        <Test animal={this.state.animal} />
      </div>
    );
  }
}

Seeing this, I believe you should be able to answer the second and third questions, but let’s take a look again~

Q: If I Test components React.Component replaced React.PureComponent , the above results the same? If it is not the same, why?

A: Because each pass props in onClick are App components handleTestClick , using both PureComponent , relatively shallow so every time is the same, it will not print Test componentDidUpdate up.

Question: What if I modify this line of code <Test onClick={this.handleTestClick} /> to <Test onClick={() => {}} /> ?

Answer: Although PureComponent is used, because App will redeclare a method every time the render Test last time, so each click will still print Test componentDidUpdate .

Remaining content to add

In addition to the above two APIs, the other APIs are more or less just their revisions, so I will talk about them together.

memo

React.memo in my opinion is PureComponent stateless component versions, if using a class to use PureComponent , if using a stateless component to use memo .

import React from 'react';

export default React.memo(function Test() {
  return <div>Test Component</div>;
});

// 它也可以接收第二个参数, 类似 shouldComponentUpdate
// 两个参数上次的props, 和当前的props
// 不传默认情况它们两个做浅比较, 传了由你自己控制

Note: This method returns the value shouldComponentUpdate contrast, the return value true not re-render the component.

useCallback and useMemo

These two APIs are React Hook , why should they be put together? Because they are very similar, according to the official document useCallback(fn, deps) equivalent to useMemo(() => fn, deps) .

Because of React Hook , I rarely write the class component now. The reason is that I believe the friends who have used it will know it. This article does not elaborate on this aspect. I just want to ask you one more question: handleClick method be redefined every time?

import React from 'react';

export default function Test() {
  const handleClick = () => console.log('click');
  return <div onClick={handleClick}>Test</div>
}

The answer is yes, you can verify if you don't believe it.

import React, { useState } from 'react';

// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Set
let set = new Set();

export default function App() {
  const [val, setVal] = useState(0);
  
  // 对比两种方式可以看出区别
  // const handleClick = useCallback(() => console.log('click'), []);
  const handleClick = () => console.log('click');
    
  // set 存的唯一值, 每次都会添加一个新值
  set.add(handleClick);
  console.log(set.size);

  return (
    <div>
      {/* 如果 Test 是个特别复杂的组件, handleClick 每次变化都会导致它重新渲染 */}
      <Test onClick={handleClick}>Test</Test>
      <div onClick={() => setVal(val + 1)}>click</div>
    </div>
  );
}

Usage and explanation can be seen from the above examples, so there is no additional explanation. Sometimes we need a value in addition to the function that needs to be cached. At this time, we need to use useMemo . The difference between the two is also in this, just look at the usage.

import React, { useState, useMemo } from 'react';

let set = new Set();

export default function App() {
  const [val, setVal] = useState(0);
    
  // 对比三种方式可以看出区别
  // const obj = useMemo(() => ({ label: 'Test', value: 'test' }),[]);
  // const obj = 'obj';
  const obj = { label: 'Test', value: 'test' };

  set.add(obj);
  console.log(set.size);

  return (
    <div>
      {/* 如果 Test 是个特别复杂的组件, obj 每次变化都会导致它重新渲染 */}
      <Test obj={obj}>Test</Test>
      <div onClick={() => setVal(val + 1)}>click</div>
    </div>
  );
}

This involves a JavaScript , which is similar to the above. You can try obj is equal to a basic type.

Profiler

Finally, let's Profiler about 060f4f8d40e9d9, which is used to measure the overhead caused by the DOM tree rendering wrapped by it, to help you troubleshoot performance bottlenecks, and look directly at the usage.

import React, { Profiler, useState } from 'react';

function Test() {
  return <div>Test</div>;
}

const callback = (
  id, // 发生提交的 Profiler 树的 “id”
  phase, // "mount" (如果组件树刚加载) 或者 "update" (如果它重渲染了)之一
  actualDuration, // 本次更新 committed 花费的渲染时间
  baseDuration, // 估计不使用 memoization 的情况下渲染整颗子树需要的时间
  startTime, // 本次更新中 React 开始渲染的时间
  commitTime, // 本次更新中 React committed 的时间
  interactions // 属于本次更新的 interactions 的集合
) => {
  console.log(
    id,
    phase,
    actualDuration,
    baseDuration,
    startTime,
    commitTime,
    interactions
  );
};

export default function App() {
  const [val, setVal] = useState(0);
  return (
    <div>
      <Profiler id="test" onRender={callback}>
        <Test>Test</Test>
      </Profiler>
      <div onClick={() => setVal(val + 1)}>click</div>
    </div>
  );
}

last of the last

React that I know are all on it. To be honest, there are really too few scenarios that require performance optimization. This is why interviewers always like to ask questions in this area when they choose people, because Most people have not paid attention, and what we want is that small group of people. Therefore, learning more knowledge is really good for yourself, and understanding more content can you better understand other people's code.


青湛
1.1k 声望45 粉丝

我是光