Usecallback and usememo for real performance optimization

kiner-tang 2021-05-03 14:48:49
usecallback usememo real performance optimization


useMemo

When we're writing a functional component , It is often encountered that independent variable changes cause some methods to be re executed , Cause performance waste , This is the time , We can consider using useMemo Cache our values , Only if the dependency of this value changes , We take the reexecution method to get the new value , Otherwise, get the last value directly from the cache and return it directly .

Scenarios and examples

Let's take a look at the following pseudo code :

function Demo(){
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>("");
const showCount = () => {
console.log(" Yes showCount");
let sum = 0;
for(let i=0;i<count;i++){
sum+=i;
}
return sum;
};
return (
<div>
<h2> This is a useMemo Test case ( Not optimized )</h2>
<p> Cumulative results : {showCount()}</p>
<p> Counter : {count}</p>
<button onClick={()=>setCount(count + 1)}> increase </button>
<input value={name} onChange={e => setName(e.target.value)} />
</div>
);
}
 Copy code 

The above pseudo code is not used useMemo Optimized code , When we click increase Button , It can really achieve our expected effect , Both the accumulation result and the counter have changed . But when we set the text box to be worth it , Now it's just changed name, It hasn't changed count, Our expectation is not to start again showCount Method to perform recalculation , But the above code will still trigger repeatedly . We can imagine that , If in showCount Method is an extremely complex and time-consuming calculation , Then this code that looks like a few lines may cause the whole website to jam or even crash .

that , Let's use useMemo Modify the above code :

function Demo(){
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>("");
// Use useMemo Treatment calculation method , Only if the dependent variable count The recalculation is triggered only when the change is made to get a new result , Otherwise, the cache of the last calculation result will be directly obtained and returned , Avoid repeated calculation without objection 
const showCount = useMemo(() => {
console.log(" Yes showCount");
let sum = 0;
for(let i=0;i<count;i++){
sum+=i;
}
return sum;
}, [count]);
return (
<div>
<h2> This is a useMemo Test case ( Not optimized )</h2>
<p> Cumulative results : {showCount}</p>
<p> Counter : {count}</p>
<button onClick={()=>setCount(count + 1)}> increase </button>
<input value={name} onChange={e => setName(e.target.value)} />
</div>
);
}
 Copy code 

The use of,

useMemo(nextCreateFn, deps)

Official explanation :

Return to one memoized value .

hold “ establish ” Functions and dependency arrays are passed in as arguments useMemo, It recalculates only when a dependency changes memoized value . This optimization helps avoid high overhead calculations every time you render .

remember , Pass in useMemo Will be executed during rendering . Please do not perform render independent operations inside this function , Operations such as side effects belong to useEffect Scope of application , instead of useMemo.

If no dependency array is provided ,useMemo The new value is calculated every time you render .

** You can take useMemo As a means of performance optimization , But don't take it as a guarantee in the meaning of idioms .** future ,React May choose “ Forget ” Some of the old memoized value , And recalculate them the next time you render , For example, releasing memory for off screen components . First write without useMemo Can also be executed in the case of —— Then add... To your code useMemo, To optimize performance .

among nextCreateFn We need to pass in a function , The function used to calculate the target result , This function needs a return value , The return value of the function is the final result of our calculation

deps Is a dependency array , We need to put the external state used in the function , That is, dependency variables are added in , thus , When dependency doesn't change , We can get the result of the last cache and return it directly , There is no need to repeat nextCreateFn The result of the calculation is . When deps by null yes , Will be recalculated every time you render , In this way, we lose this hooks The meaning of . therefore , If you don't send this dependent parameter in time, the program will not report an error , We also need to define the function dependency and pass in the dependency array during development , Otherwise, there is no need to use this hooks.

Source code analysis

React Source code useMemo The implementation of the

// This function is used to compare whether the dependencies of two dependency arrays are the same 
function areHookInputsEqual(
nextDeps: Array<mixed>,
prevDeps: Array<mixed> | null,
) {
// Delete some development environment debugging code that has nothing to do with logic here 
// Last time I relied on null, The current dependence is not null Words , That must be different , Dependence changes , Therefore return false
if (prevDeps === null) {
// Delete some development environment debugging code that has nothing to do with logic here 
return false;
}
// Delete some development environment debugging code that has nothing to do with logic here 
// Loop through each dependency , And compare the last dependency with the current dependency , As long as there is one different , Then return directly false
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
if (is(nextDeps[i], prevDeps[i])) {
continue;
}
return false;
}
// At the end of the cycle , All dependencies have not changed , It's all the same , return true
return true;
}
// The following is a React To realize useMemo The main code of logic 
// We can see from the following code that React In execution useMemo In fact, it is divided into two stages , One is to mount , One is to update 
// When mounting, we will first execute the calculation method we passed in , namely : nextCreate, The first calculation result is obtained nextValue, Then save the result of the calculation and the dependency array in memoizedState It's a cache , It is convenient to compare and get cache results when updating . Finally, the result of the first calculation is returned as the result of the first rendering 
function mountMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
// When a component is re rendered because of some operation , The last dependency array will be taken out , Compared to the current dependency array , If you find that the state of the dependency has not changed , Directly from memoizedState The last calculation result cached in returns , No need to re execute nextCreate Recalculate . Otherwise, recalculate , And cache the latest calculation results and the new dependency array , And return the new calculation result as the result of this rendering 
function updateMemo<T>(
nextCreate: () => T,
deps: Array<mixed> | void | null,
): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
// Assume these are defined. If they're not, areHookInputsEqual will warn.
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
const nextValue = nextCreate();
hook.memoizedState = [nextValue, nextDeps];
return nextValue;
}
 Copy code 

useCallback

Scenarios and examples

We often bind events to elements , Or pass some functions to subcomponents in the form of attributes , If in a functional component , If a function without any processing is passed to a child component in the form of an attribute , that , Once any state of the parent component changes for re rendering , Because each function is a new reference, the sub components will be re rendered due to the change of properties . Let's take a look at the following example to make it clear :

function Demo(){
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>("");
const showCount = () => {
console.log(" Yes showCount");
let sum = 0;
for(let i=0;i<count;i++){
sum+=i;
}
return sum;
};
return (
<div>
<h2> This is a useMemo Test case ( Not optimized )</h2>
<p> Counter : {count}</p>
<button onClick={()=>setCount(count + 1)}> increase </button>
<input value={name} onChange={e => setName(e.target.value)} />
<Child onClick={showCount} />
</div>
);
}
function Child(props) {
console.log("child rerender!!");
return <div onClick={props.onClick}> This is the child node </div>
}
 Copy code 

In the example code above , We will showCount Functions are passed as properties to subcomponents Child, When our parent component Demo Any state of, such as :count and name When there is a change , Will be recreated showCount function , Trigger due to inconsistent function references Child Re rendering of components . however , our showCount The method is obviously the same as ours name This state doesn't matter , therefore , We hope only when count It's triggered when the state changes Child Re rendering of components . that , At this time we can use useCallback 了 .

function Demo(){
const [count, setCount] = useState<number>(0);
const [name, setName] = useState<string>("");
// Use useCallback Optimization function , If and only if count When the change , We just changed our callback , Otherwise, get the cached function directly , Keep references consistent 
const showCount = useCallback(() => {
console.log(" Yes showCount");
let sum = 0;
for(let i=0;i<count;i++){
sum+=i;
}
return sum;
}, [count]);
return (
<div>
<h2> This is a useMemo Test case ( Not optimized )</h2>
<p> Counter : {count}</p>
<button onClick={()=>setCount(count + 1)}> increase </button>
<input value={name} onChange={e => setName(e.target.value)} />
<Child onClick={showCount} />
</div>
);
}
function Child(props) {
console.log("child rerender!!");
return <div onClick={props.onClick}> This is the child node </div>
}
 Copy code 

The use of,

useCallback(callback, deps)

Official explanation :

Return to one memoized Callback function .

Pass in the inline callback function and dependency array as parameters useCallback, It will return the memoized edition , The callback function is updated only when a dependency changes . When you pass the callback function to the optimized and use reference equality to avoid unnecessary rendering ( for example shouldComponentUpdate) When you are a child of , It will be very useful .

useCallback(fn, deps) amount to useMemo(() => fn, deps).

useCallback And useMemo The users are basically the same , Two parameters are passed in , The first is the function , The second is dependency arrays , The difference is ,useMemo Will execute the function we passed in the past to calculate the target result , and useCallback Then we just cache the function we passed in and return it , Not going to execute it .

Source code analysis

React Source code useCallback The implementation of the

// And useMemo Different ,useCallback Not to execute callback Get the results , Instead, it directly caches and returns callback
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = mountWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
hook.memoizedState = [callback, nextDeps];
return callback;
}
// It's the same with updates , Except not to execute callback Beyond the results , The others are all with useMemo Same 
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
hook.memoizedState = [callback, nextDeps];
return callback;
}
 Copy code 

Conclusion

What we're talking about this time is actually very simple , It is estimated that you will be able to make a comprehensive understanding in a few minutes , The reason why we take this point as a topic of discussion , Because with the development of our project for functional components and hooks More and more frequently used , Our business functions are becoming more and more detailed , The components are getting smaller and smaller , We need to optimize the performance from every small point , Otherwise, when a large number of unoptimized components are introduced on the page , It may enlarge some small performance problems infinitely , Eventually the page gets stuck or even crashes , Make good use of useMemo and useCallback, Let's try to avoid this as much as possible , Let the project run more smoothly and efficiently .

版权声明
本文为[kiner-tang]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/05/20210503144051227P.html

  1. 浅谈 React 中的 XSS 攻击
  2. XSS attack in react
  3. 自学前端教程整理,附不容错过的前端100篇文章合集
  4. Self taught front-end tutorial collation, with a collection of 100 front-end articles that can not be missed
  5. 使用OpenTracing跟踪Go中的HTTP请求延迟
  6. Using opentracing to track HTTP request latency in go
  7. Encapsulating databinding allows you to write less than 10000 lines of code
  8. 03-HTML5标签-HTML5极速入门
  9. 03-html5 tag-html5 quick start
  10. LayUI - 极易上手拿来即用的前端 UI 框架
  11. Layui - easy to use front end UI framework
  12. Interpretation of lodash source code (1)
  13. Why is the first parameter of node family callback error?
  14. 报告:JavaScript 开发者达1380 万,C#超越 PHP,Rust 增长最快
  15. Report: Javascript developers reach 13.8 million, C surpasses PHP, and rust grows fastest
  16. 小白前端入门笔记(10),怎么设置网站内部的超链接?
  17. How to set up hyperlinks inside the website?
  18. Using node and socket to realize online chat room
  19. The core competitiveness of Vue: data bidirectional binding
  20. React configuration agent
  21. CSS layout
  22. Application scenario explanation of Vue dynamic component
  23. Redux learning notes 04 -- using multiple reducers to manage data
  24. After three months of typescript writing, what have I learned?
  25. Node family - what is a callback?
  26. React -- a simple implementation of render & create element
  27. JS learning simple usage of jquery
  28. Seamless love
  29. 小白前端入门笔记(12),设置哑链接
  30. Small white front-end entry notes (12), set dumb links
  31. Vue2. X opens composition API and TSX
  32. Interview record and thinking of social recruitment for one and a half years (Alibaba, Tencent, baidu offer)
  33. Flex learning notes
  34. The most essential closure article in the eastern hemisphere
  35. 2021-05-03 hot news
  36. Sword finger offer -- reverse order pair in array (JS Implementation)
  37. Working process of scaffold
  38. Use decorator mode to strengthen your fetch
  39. [JS] scope (Introduction)
  40. Employment information statistics network (interface document)
  41. Analysis of MVC
  42. [middle stage] please stay and join me in the backstage
  43. Understanding front end garbage collection
  44. [continuous update] front end special style implementation
  45. Flutter product analysis and package reduction scheme
  46. XPath positioning
  47. 前端开发css中的flex布局的使用
  48. The use of flex layout in front end development CSS
  49. JQuery核心函数和静态方法
  50. JQuery core functions and static methods
  51. Node family - understanding of blocking and non blocking
  52. 热点微前端Microfrontend的讨论:谷歌AdWords是真实的微前端
  53. Vue source code analysis (2) initproxy initialization proxy
  54. What's TM called react diff
  55. Summary of common front end data structure
  56. Useeffect in hooks
  57. [encapsulation 02 design pattern] Command pattern, share meta pattern, combination pattern, proxy pattern, strategy pattern
  58. Front end notes: virtual Dom and diff of vue2. X
  59. The best code scanning plug-in of flutter
  60. The simplest plug-in for rights management of flutter