preface
Memo, useCallback, useMemo.
After reading this article, you can quickly understand their respective usage scenarios, using a real development scenario as an example: Optimizing rendering is a constant skill when developing with React. Most commonly, when the parent component is rendered, the associated child component is also re-executed.
Such as:
// in App.js import React, { useState, useMemo, memo } from "react"; import ChildComponent from "./ChildComponent"; export default function App() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>{count}</h2> <button onClick={handleClick}>click</button> <ChildComponent /> </div> ); // in ChildComponent.js const ChildComponent = (props) => { console.log("ChildComponent Running"); Return <div>{' here is ChildComponent '}</div>; }; export default ChildComponent;Copy the code
The state in the parent element changes after each click, but the child element does not depend on the parent element’s state, but the child element is also reexecuted.
React.PureComponent
If the component is implemented using a class, you can use react. PureComponent to define the component.
class Greeting extends React.PureComponent { render() { return <h1>Hello, {this.props.name}</h1>; }}Copy the code
The react. PureComponent differs from the react.componentcomponent in that the PureComponent implements shouldComponentUpdate, which allows shallow comparisons between props and state to optimize component rendering.
React.Memo
Use React. Memo to wrap the component. The react. memo wrapped component will use the last rendering directly without using the props operation. By default, React. Memo does shallow comparisons to complex objects, or you can pass a custom comparison function as a second argument to React.
const ChildComponentMemo = memo(ChildComponent, (prevProps, nextProps) => {
// return true or false;
});
Copy the code
useCallback
But the React. Memo is flawed. If a reference data type is passed in, ChildComponet will still be re-executed when modifying state that is independent of the child component.
// in ChildComponent.js const ChildComponent = (props) => { console.log("ChildComponent Running"); Return (<div> {' here is ChildComponent '} <button onClick={props. HandleAddCat}>add cat</button> </div>); }; // in APP.js export default function App() { const [dogCount, setDogCount] = useState(0); const [catCount, setCatCount] = useState(0); const handleAddDog = () => { setDogCount(dogCount + 1); }; Const handleAddCat = () => {setCatCount(catCount + 1); }; return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>dog:{dogCount}</h2> <h2>cat:{catCount}</h2> <button onClick={handleAddDog}>click</button> <ChildComponentMemo handleAddCat={handleAddCat}/> </div> ); }Copy the code
This is because when state changes after click, the parent component (app.js) will re-execute, causing the reference type to be recreated (the reference address changed), and the ChildComponent (childcomponent.js) will think the props have changed and re-execute.
Use useCallback to cache reference addresses:
// in APP.js export default function App() { const [dogCount, setDogCount] = useState(0); const [catCount, setCatCount] = useState(0); const handleAddDog = () => { setDogCount(dogCount + 1); }; // When dogCount is changed, Const handleAddCat = useCallback(() => {setCatCount(catCount + 1); }, [catCount]); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>dog:{dogCount}</h2> <h2>cat:{catCount}</h2> <button onClick={handleAddDog}>click</button> <ChildComponentMemo handleAddCat={handleAddCat}/> </div> ); }Copy the code
useMemo
Many people ask what is the difference between useMemo and useCallback?
In summary: useCallback caches functions, useMemo caches values after the function is executed.
That is useCallback(fn, [deps]) is equivalent to useMemo(() => fn, [deps])
// in App.js function App() { const [dogCount, setDogCount] = useState(0); const [catCount, setCatCount] = useState(0); const handleAddDog = () => { setDogCount(dogCount + 1); }; Const computeValue = (catCount) => {// Assume there is a lot of computeconsole. log('computeValue Running'); return catCount + 1; Const value = computeValue(catCount); const value = computeValue(catCount); console.log('value: ', value); }Copy the code
When the dogCount is increased by click, the computeCount is still executed, but its calculation has nothing to do with dogCount and the result of its execution is the same.
UseMemo can be used to cache the calculated values:
// in App.js function App() { const [dogCount, setDogCount] = useState(0); const [catCount, setCatCount] = useState(0); const handleAddDog = () => { setDogCount(dogCount + 1); }; Const computeValue = (catCount) => {// Assume there is a lot of computeconsole. log('computeValue Running'); return catCount + 1; } // With useMemo, the value is recalculated only when the catCount changes. Const value = useMemo(() => computeValue(catCount), [catCount])}Copy the code