preface

In this paper, the sample

Based on the sample

Requirement: Write a parent and child component

The parent component

import React, { useState } from 'react'
import Child from './child'
export default function Parent(props: any) {
	const [num, setNum] = useState(0)
	const handleClick = () = > {
		setNum(num + 1)}return (
		<div>
			<h2>{num}</h2>
			<button onClick={handleClick}>Change the num</button>{/* reference subcomponent */}<Child />
		</div>)}Copy the code

Child components

export default function Child(props: any) {
	console.info('Child component rendered')
	return <div>I am a child component</div>
}

Copy the code

Phenomenon:

Every time you click the “Change Num” button, the console iswillIt says “Child component rendered”

React.memo

The front face component does not depend on any properties of the parent component, but it is rerendered every time the parent component changes, which can be optimized using React.memo.

define

const MyComponent = React.memo(function MyComponent(props) {
  /* Render with props */
});
Copy the code

Memo is the same as PureComponent. It is a higher-order component. By default, a light comparison of props is made.

If you want to customize the comparison process, you can do so with the second parameter:

function MyComponent(props) {
  /* Render with props */
}
function areEqual(prevProps, nextProps) {
  Return true if passing nextProps to render returns the same result as passing prevProps to render, false otherwise */
}
export default React.memo(MyComponent, areEqual);
Copy the code

To optimize the

import React, { useState } from 'react'
import Child from './child'
const MemoChild = React.memo(Child) // Add code
export default function Parent(props: any) {
	const [num, setNum] = useState(0)
	const handleClick = () = > {
		setNum(num + 1)}return (
		<div>
			<h2>{num}</h2>
			<button onClick={handleClick}>Change the num</button>{/* Reference child component */} {/*<Child />*/} {/* add code */}<MemoChild /> 
		</div>)}Copy the code

Phenomenon:Every time you click the “Change Num” button, the console isNo longerIt says “Child component rendered”Is the example perfect? This works if the parent and child components do not communicate. However, there is still a problem when the parent component tries to pass data and events to the child component.

Example of a parent component passing events to a child component

Change the parent component code to:

import React, { useState } from 'react'
import Child from './child'
const MemoChild = React.memo(Child)
export default function Parent(props: any) {
	const [num, setNum] = useState<number>(0)
	const handleClick = () = > {
		setNum(num + 1)}const handleChange = () = > {
		console.info(2323)}return (
		<div>
			<h2>{num}</h2>
			<button onClick={handleClick}>Change the num</button>{/ * reference subcomponents * /} {new code / * * /} {/ * parent component pass handleChange events to subcomponents * /}<MemoChild handleChange={handleChange}/> 
		</div>)}Copy the code

Phenomenon:

Every time you click the “Change Num” button, the console iswillIt says “Child component rendered”When passing an event to a child component, click Change NUM and the console will print “child component rendered” again, indicating that the child component has been rendered again.

Analyze the reasons:

First, a component is rerendered, usually due to one of three conditions:

  1. The state of the component itself has changed
  2. The parent component is rerendered, but the parent’s props are unchanged
  3. The parent component was rerendered, and the props passed by the parent changed

Obviously, the first is not, because the child component does not have any state.

The second one has been optimized out with React.memo, so it is not the same.

Well, that means the third — props for passing.

So why did the handleChange function change?

Since each time you rerender a functional component, the function call is executed again from scratch, the handleChange function must have changed between the two times it was created, causing the subcomponent to rerender.

Process:

  1. The user clicks the “Change NUM” button
  2. The parent component changes the num status
  3. A state value change causes the parent component to rerender
  4. The handleChange function registers again
  5. HandleChange props
  6. The child component is rerendered

useCallback

Find out why, and try to keep the reference to the function consistent when rerendering without changing the function

define

const memoizedCallback = useCallback(
  () = > {
    doSomething(a, b);
  },
  [a, b],
);
Copy the code

Returns a memoized callback function.

Passing the inline callback function and the array of dependencies as arguments to useCallback returns a memoized version of the callback function that is updated only if one of the dependencies changes. This is useful when you pass callback functions to child components that have been optimized and use reference equality to avoid unnecessary rendering (such as shouldComponentUpdate).

UseCallback (fn, deps) equals useMemo(() => fn, deps).

To optimize the

// Add useCallback
import React, { useState, useCallback } from 'react'
import Child from './child'
const MemoChild = React.memo(Child) 
export default function Parent(props: any) {
	const [num, setNum] = useState<number>(0)
	const handleClick = () = > {
		setNum(num + 1)}// Modify the code
  // Remember the handleChange by useCallback and pass the handleChange to MemoChild
	const memoizedCallback = useCallback(() = > {
		console.info(2323)
	},[])
	return (
		<div>
			<h2>{num}</h2>
			<button onClick={handleClick}>Change the num</button>{/* reference subcomponent */}<MemoChild handleChange={memoizedCallback} />
		</div>)}Copy the code

Phenomenon:

Every time you click the “Change Num” button, the console isNo longerIt says “Child component rendered”You can see that the child component is not being rerendered, so is it finished?

No, in addition to events, state is passed between parent and child components

Example of a parent component passing state to a child component

import React, { useState } from 'react'
import Child from './child'
const MemoChild = React.memo(Child) // Add code
export default function Parent(props: any) {
	const [num, setNum] = useState<number>(0)
	const [name] = useState<string>()
	const handleClick = () = > {
		setNum(num + 1)}return (
		<div>
			<h2>{num}</h2>
			<button onClick={handleClick}>Change the num</button>{/ * reference subcomponents * /} {new code / * * /} {/ * parent component pass name state to subcomponents * /}<MemoChild name={name} />
		</div>)}Copy the code

Phenomenon:

Every time you click the “Change Num” button, the console isDon’t“Child component rendered” pops up.Isn’t that normal? Are you kidding me? No, please read on

The name passed above is a primitive type. What if it is an object? Change the code as follows:

import React, { useState } from 'react'
import Child from './child'
const MemoChild = React.memo(Child) 
export default function Parent(props: any) {
    const [num, setNum] = useState<number>(0)
    const handleClick = () = > {
        setNum(num + 1)}return (
        <div>
            <h2>{num}</h2>
            <button onClick={handleClick}>Change the num</button>{/ * reference subcomponents * /} {/ * modify the code * /} {/ * parent component pass name state to subcomponents * /} {/ *<MemoChild name={name} />* /}<MemoChild person={{name:}} />
        </div>)}Copy the code

Phenomenon:

Every time you click the “Change Num” button, the console iswill“Child component rendered” pops up.In real business, the object passed is certainly more complex, but I’m just here to show that any object will cause the subcomponents to be rerendered.

The reason for passing events is the same as above. If you pass an object, each time you rerender it, it points to the new reference. If you do a light comparison with the child component React.

UseCallback is optimized to handle parent-child passing events, and useMemo is used for passing state.

useMemo

define

 const memoizedValue = useMemo(() = > computeExpensiveValue(a, b), [a, b]);
Copy the code

Pass the “create” function and dependency array to useMemo as parameters, and it will only recalcalculate memoized if one of the dependencies changes. This optimization helps to avoid high overhead calculations every time you render.

To optimize the

import React, { useState,useMemo } from 'react'
import Child from './child'
const MemoChild = React.memo(Child)
export default function Parent(props: any) {
	const [num, setNum] = useState<number>(0)
	const handleClick = () = > {
		setNum(num + 1)}return (
		<div>
			<h2>{num}</h2>
			<button onClick={handleClick}>Change the num</button>{/* use useMemo */}<MemoChild person={useMemo(()= >({name:" zhang SAN "}),[])} /></div>)}Copy the code

Phenomenon:

Every time you click the “Change Num” button, the console isDon’t“Child component rendered” pops up.

Now you can pass states and events with confidence.

conclusion

React Hooks offer more flexibility and freedom than class components, but are also more demanding for developers. Performance problems can occur due to improper use of Hooks. Memo, useMemo, and useCallback are all used to improve performance.

  1. Memo is equivalent to shouldComponentUpdate
  2. UseCallback makes shouldComponentUpdate work properly
  3. UseMemo avoids frequent expensive calculations, and can certainly be used with props states like this article

So when do you use these apis?

In fact, in simple applications, try to use it less often, because in simple applications, even re-rendering does not consume much resources, whereas in these apis, the second parameter is compared every time, which consumes resources.

Of course, use Memo whenever possible for expensive components.