React Hooks learning - 03 useMemo, react memo,useCallback,useRef

useMemo hook function

The behavior of useMemo is similar to the calculation attribute in Vue. It can monitor the change of a certain data and calculate a new value according to the change value. The calculated new value can participate in view rendering.

useMemo caches the calculation results. If the monitoring value does not change, it will not be recalculated even if the component is re rendered. This behavior helps to avoid expensive calculation on each rendering.

use

useMemo receives an array of computed functions and dependencies.

  • Calculation function: when the monitored data changes, execute this function, and the value returned by the function is the new value calculated
  • Dependency array: data to be monitored
    • If not, the calculation function is executed at each render
    • If an empty array is passed, it will be executed only once at the initial load

The value returned by useMemo is the value returned by the calculation function.

import { useState, useMemo } from 'react'

function App() {
  const [count, setCount] = useState(0)

  const result = useMemo(() => {
    console.log('The test is being modified bool Will it be recalculated')
    return count * 2
  }, [count])

  const [bool, setBool] = useState(true)

  return (
    <div>
      <span>{result}</span>
      <span>{count}</span>
      <button onClick={() => setCount(count+1)}>+1</button>
      <br/>
      <span>{bool ? 'really' : 'false'}</span>
      <button onClick={() => setBool(!bool)}>change Bool</button>
    </div>
  );
}

export default App;

React.memo method

Introduce use

The memo method is a high-order component for performance optimization.

If the props of the component does not change, you can prevent the component from re rendering and directly reuse the results of the last rendering.

import React, { memo, useState } from 'react'

/*
// count Each change renders the Foo component
function Foo() {
  console.log('Foo Component re rendered ')
  return (
    <div>Foo Component < / div >
  )
}
*/

// The count change does not re render the Foo component
const Foo = memo(function() {
  console.log('Foo The component is re rendered')
  return (
    <div>Foo assembly</div>
  )
})

function App() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => setCount(count+1)}>+1</button>
      <Foo />
    </div>
  );
}

export default App;

Differences from shouldComponentUpdata and PureComponent of class components

Same point

  • Scenario: when the page data changes (including the change of reference address caused by object re assignment), the page (including components) will be re rendered, which is very performance consuming.
  • Function: when the page data changes, you can compare the old and new data. If the value of the data does not change, or the sub component does not rely on the changed data, you can avoid the re rendering of the component or page.
  • It's all shallow contrast

difference

  • shouldComponentUpdata
    • The lifecycle function can only be used in class components
    • Comparison data types: state and props
    • You can customize the comparison logic
    • The function returns true to re render the component and false to not render
  • React.PureComponent
    • Component inheritance class
    • Class components that inherit PureComponent cannot use shouldComponentUpdata
    • Comparison data types: state and props
    • The built-in comparison logic is equivalent to the default definition of shouldComponentUpdata, but the comparison logic cannot be customized
  • React.memo
    • High order components can only be used in functional components
    • Comparison data type: props
    • The second parameter receives a function, defines the comparison logic, returns true, does not re render, and returns false to re render

useCallback hook function

useCallback is also used for performance optimization. It can cache functions to get the same function instance when the component is re rendered.

Pass method to component

import { useState, memo } from 'react'

const Foo = memo(function(props) {
  console.log('Foo The component is re rendered')
  return (
    <div>
      <button onClick={() => props.resetCount()}>Reset Count</button>
    </div>
  )
})

function App() {
  const [count, setCount] = useState(0)

  const resetCount = () => {
    setCount(0)
  }

  return <div>
    <span>{count}</span>
    <button onClick={() => setCount(count+1)}>+ 1</button>
    <Foo resetCount={resetCount} />
  </div>
}

export default App

The App component will be re rendered when the count is updated (the App function will be re executed), and the resetCount will be redefined, which is different from the function instance passed to Foo before, so Foo will be re rendered.

As a result, Foo components always re render when the value of unused count changes.

Cache function

In order to avoid unnecessary repeated rendering, useCallback can be used to cache the resetCount method. When the App re renders, the resetCount method in the cache is obtained and passed to the Foo component. Since the resetCount (reference address) in the cache has not changed, the Foo component will not be rendered additionally.

useCallback can also accept a dependency array as the second parameter:

  • If not, the function is redefined every time the component is rendered (equivalent to not using useCallback)
  • If the array is not empty, the component will compare the array when rendering. If the array changes, redefine the function
  • If the array is empty, it will be executed only once when the component is mounted.
const resetCount = useCallback(() => {
  setCount(0)
}, [])

or

const resetCount = useCallback(() => {
  setCount(0)
}, [setCount])

useRef hook function

useRef has two functions:

  • Get DOM element object
  • Save data across component cycles

Get DOM element object

useRef(initial) returns a mutable ref object. The object has only one current attribute. The initial value is initial.

When the ref object is passed to the ref attribute of the component or element, the current of the ref object points to the DOM element object.

When the node changes, it will only change the current attribute of the ref object and will not trigger the component to re render.

useRef for functional components:

import { useRef } from 'react'

function App() {
  const box = useRef()

  return <div>
    <button onClick={() => console.log(box)}>obtain DIV</button>
  </div>
}

export default App

Class components use createRef:

import React from 'react'

class App extends React.Component {
  constructor(props) {
    super(props)
    this.box = React.createRef()
  }

  render() {
    return <div ref={this.box}>
      <button onClick={() => console.log(this.box)}>obtain DIV</button>
    </div>
  }
}

export default App

Save data (across component cycles)

The ref object returned by useRef remains unchanged throughout the life cycle of the build. Each time it is re rendered, the same ref object is returned.

If the current attribute of ref object changes, it will not cause the component to re render.

In essence, this ref object looks like it can be in its A "box" with variable values is saved in the current attribute.

Therefore, useRef can also be used to save data across component cycles:

  • Even if the component is re rendered, the saved data is still there.
  • The saved data is changed and the component re rendering is not triggered.

Difference from the data saved by useState: useState saves state data. When the state changes, it will trigger the component to re render.

Stop timer case

Create a timer in the side effect function, modify the status regularly, and click the button to stop the timer.

The following methods will not succeed:

import { useState, useEffect } from 'react'

function App() {
  const [count, setCount] = useState(0)

  // When the component is re rendered, the timerId will be reset
  let timerId = null

  useEffect(() => {
    timerId = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  }, [])

  const stopCount = () => {
    clearInterval(timerId)
  }

  return <div>
    {count}
    <button onClick={stopCount}>stop it</button>
  </div>
}

export default App

Using useState

You can use useState to save the timerId as a state to avoid being reset when re rendering.

import { useState, useEffect } from 'react'

function App() {
  const [count, setCount] = useState(0)

  // Use useState to ensure that the timerId is not reset after the component is re rendered
  const [timerId, setTimerId] = useState(null)

  useEffect(() => {
    setTimerId(setInterval(() => {
      setCount(count => count + 1)
    }, 1000))
  }, [])

  const stopCount = () => {
    clearInterval(timerId)
  }

  return <div>
    {count}
    <button onClick={stopCount}>stop it</button>
  </div>
}

export default App

Using useRef

import { useState, useEffect, useRef } from 'react'

function App() {
  const [count, setCount] = useState(0)

  // Use useRef to save data, which remains unchanged throughout the life cycle
  const timerId = useRef(null)

  useEffect(() => {
    timerId.current = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  }, [])

  const stopCount = () => {
    clearInterval(timerId.current)
  }

  return <div>
    {count}
    <button onClick={stopCount}>stop it</button>
  </div>
}

export default App

Keywords: React

Added by Fluf on Fri, 11 Feb 2022 12:50:43 +0200