We start with Hooks, which are new to React since version 16.8. There is no need to use class to develop components, making the code more concise. However, this API is the core behind React, so it’s worth checking out.

If you haven’t read this and written Hooks, I suggest you read the official documentation first. First understand the basic writing, operating mechanism is better to understand some knowledge points below.

First let’s define the parent and child components: DC, JOKER. Let’s do it class first, PureComponent, because shouldComponentUpdate is unnecessary.

import React, { PureComponent, } from 'react';
class JOKER extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      count: props.count,
    }
  }
  componentWillReceiveProps(nextProps) {
    console.log('I am JOKER\'s componentWillReceiveProps--->'); this.setState({ count: nextProps.count }); } render() { console.log('I am JOKER\'s render--->');
    const { count } = this.state;
    return (
      <div>
        <p style={{ color: 'red' }}>JOKER: You clicked {count} times</p>
      </div>
    );
  }
}
class DC extends PureComponent {
  constructor() {
    super();
    this.state = {
      count: 0,
    };
  }
  render() {
    const { count } = this.state;
    return (
      <div>
        <p>You clicked {count} times</p> <button onClick={() => this.setState({ count: count + 1, })}> Click me </button> <JOKER count={count} /> </div> ); }}Copy the code

Copy

The logic of the DEMO is very simple: there is a button in the DC. Click the button to update the internal state count. After receiving the props update, the JOKER also updates his own state. The effect is as follows:

Received new props, performing componentWillReceiveProps, then setState, final render. ComponentWillReceiveProps, render are executed once, see the log from the console and all is in line with expectations.

Ok, let’s rewrite this logic in Hooks, same DC, JOKER.

import React, { useState, useEffect, useMemo, } from 'react';
function JOKER(props) {
  const [count, setCount] = useState(props.count);
  useEffect(() => {
    console.log('I am JOKER\'s useEffect--->', props.count); setCount(props.count); }, [props.count]); console.log('I am JOKER\'s render-->', count);
  return (
    <div>
      <p style={{ color: 'red' }}>JOKER: You clicked {count} times</p>
    </div>
  );
}
function DC() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => {
        setCount(count + 1);
        console.log('\n');
      }}>
        Click me
      </button>
      <p>DC: You clicked {count} times</p>
      <JOKER count={count} />
    </div>
  );
}Copy the code

Copy

Same code logic, if we click the button as expected, render, useEffect should be executed once. But seriously, I’m going to do it in the browser.

From the log, JOKER rendered once with the old count, then useEffect, and then re-rendered with the new count. After a series of studies… Here omit ten thousand words (just interested can leave a message, I write an article to share)… This is a feature of the Hooks method.

If there are two states in the DC and the button button event only changes the value of the other state, then the JOKER will still execute once. Let’s change the code to look like this and see the log after execution.

function DC() {
  const [count, setCount] = useState(0);
  const [sum, setSum] = useState(0);
  return (
    <div>
      <button onClick={() => {
        setSum(sum + 1);
        console.log('\n');
      }}>
        Click me
      </button>
      <p>DC: You clicked {count} times</p>
      <p>now this is {sum} times</p>
      <JOKER count={count} />
    </div>
  );
}Copy the code

Copy

The result is as we expected, as long as the state value is changed in the Hooks component, the entire component is rerender.

Seeing this, we have to think carefully about how to avoid the performance loss caused by these extra render. If there is no solution, go back to pureComponent, because render’s memory consumption must go up exponentially when there are more components.

From the Hooks documentation we see a useMemo API.

So let’s talk a little bit about memoized. It is a scheme in JavaScript that trades memory for time. Speed up time-consuming operations by caching results and re-using the cache for the next operation. If we have computationally intensive or CPU intensive operations, we can store the results of the initial operations in memory. The operation will be executed again, and we won’t bother with our CPU, we just need to read the value that was cached in memory last time. Memoize recommended using LoDash, here is a brief explanation of how this works, you can’t just copy the code online.

function memoize(fn) {
    return function () {
        var args = Array.prototype.slice.call(arguments)
        fn.cache = fn.cache || {};
        return fn.cache[args] ? fn.cache[args] : (fn.cache[args] = fn.apply(this,args))
    }
}Copy the code

Copy

Here we do a simple experiment with this scheme, which we demonstrate using the famous Fibonacci sequence.

function fibonacci(num) {
    if (num == 1 || num == 2) {
        return1}return fibonacci(num-1) + fibonacci(num-2)
}
const memFib = memoize(fibonacci)
console.log('profiling tests for fibonacci')
console.time("non-memoized call")
console.log(memFib(6))
console.timeEnd("non-memoized call")
console.time("memoized call")
console.log(memFib(6))
console.timeEnd("memoized call")Copy the code

Copy

From the log results, the normal execution time is 0.23ms, but only 0.09ms with Memoize. Such a simple demo can make several times the difference, and if the calculation is really complex, the performance improvement will be even better.

Back to the point, since useMemo is an API for improving performance, how should we use it? The DEMO is also not provided in the documentation. Also after some time of research and try to find a correct posture.

function DC() {
  const [count, setCount] = useState(0);
  const [sum, setSum] = useState(0);
  const memoizedJOKER = useMemo(() => <JOKER count={count} />, [count]);
  return (
    <div>
      <button onClick={() => {
        // setCount(count + 1);
        setSum(sum + 1);
        console.log('---click---');
        console.log('\n');
      }}>
        Click me
      </button>
      <p>DC: You clicked {count} times</p>
      <p>now this is {sum} times</p>
      {memoizedJOKER}
    </div>
  );Copy the code

Copy

We put the component into the useMemo and then declare the values we want to listen for. In this example, we put the JOKER component into the useMemo and listen for the count value. After the execution, as we expected, JOKER did not re-execute. And then if you uncomment the setCount line, the count changes, the JOKER changes.

Use Hooks to keep in mind that when you have a lot of components that are deeply nested, you will lose a lot of memory, but you are using a good device that looks like it. Finally, Hooks should signal that functional programming is definitely on the way in future versions of React, and need to be retooled.