In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
Most people do not understand the knowledge points of this "react hooks online post-bug analysis" article, so the editor summarizes the following content, detailed content, clear steps, and has a certain reference value. I hope you can gain something after reading this article. Let's take a look at this "react hooks online bug post analysis" article.
Mandatory requirement 1. You must read the official React Hooks documentation once and for all
The key points are hooks: useState, useReducer, useEffect, useCallback, useMemo.
two。 The project must introduce the lint plug-in and open the corresponding rules
Lint plug-in must open rules:
{"plugins": ["react-hooks"], "rules": {"react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn"}}
Where react-hooks/exhaustive-deps is at least warn or error. It is suggested that the new project should be equipped with "error" directly, and the historical project should be equipped with "warn".
Keep in mind that this article is a rigid condition.
If your project does not currently open hooks lint rule, please do not write any hooks code. If you CR the code and find the other front-end project, do not open the corresponding rules, and submit the hooks code, please do not merge. This requirement is suitable for any React front-end project.
These two rules will prevent us from stepping in the pit. Although for newcomers to hooks, this process can be "painful". However, if you think these two rules are a problem for you to write code, you don't have a full grasp of hooks.
If "exhaustive-deps" is not really needed for some scenarios, you can add: / / eslint-disable-next-line react-hooks/exhaustive-deps to the code
Remember that you can only ban the code of this office, not be lazy and ban the whole file.
3. If warning caused by hooks-related lint is found, do not use global autofix
With the exception of hooks, a normal lint basically doesn't change the logic of the code, but just adjusts the writing specification. However, the lint rules of hooks are different, and changes in exhaustive-deps will lead to changes in code logic, which can easily lead to online problems, so for hooks waning, please do not do global autofix operations. Unless you make sure that every logic is fully restored.
In addition, a little sister in the company added: eslint-plugin-react-hooks has cancelled the autofix of exhaustive-deps since version 2.4.0. Therefore, please try to upgrade the lint plug-in of the project to the latest version to reduce the risk of error.
Then it is recommended to turn on the "autofix on save" of vscode. No matter what the problem is in the future, error and warning can be contained in the initial development stage as far as possible, ensuring that self-testing and testing are regular code.
Common attention point dependence problem
Dependency and closure issues are the core reasons why exhaustive-deps must be turned on. The most common error is: binding events when mount, subsequent status update errors.
Error code example: (addEventListener is used for onclick binding here just to illustrate the situation)
Function ErrorDemo () {const [count, setCount] = useState (0); const dom = useRef (null); useEffect () = > {dom.current.addEventListener ('click', () = > setCount (count + 1));}, []); return {count};}
The original idea of this code is to add 1 every time the user clicks dom,count. The ideal effect is to point all the time and keep adding. But the actual effect is that {count} cannot be added after "1".
Let's sort it out. UseEffect (fn, []) stands for triggering only when mount. That is, the first render, the fn executes once, binds the click event, and the click triggers the setCount (count + 1). At first glance, count is still the same count, it will definitely be added all the time, of course, reality is slapping in the face.
What is the nature of page rendering triggered by a state change? The essence is ui = fn (props, state, context). Props, internal state, and context changes all result in the re-execution of the render function (in this case, ErrorDemo) and return the new view.
So now the question is, the function ErrorDemo has been executed many times. Will the count inside the first function have anything to do with the count in the next few times? When I think about it, it doesn't matter anymore. So why do you know that count is 1 instead of 0 for the second time? Is the first setCount the same function as the one that follows? This involves some of the underlying principles of hooks, as well as why the declaration of hooks needs to be declared at the top of the function and is not allowed in conditional statements. I won't say much about it here.
The conclusion is that each count is a redeclared variable pointing to an entirely new data; each setCount, though redeclared, points to the same reference.
Back to the point, we know that every time we render, the internal count is actually a brand new variable. Then the click event method we bound, that is, setCount (count + 1), the count here actually refers to the count of the first render, so it has always been 0, so setCount has always set count to 1.
Then how to solve this problem?
First of all, the previous mandatory requirements should be complied with, lint rules must be added, and autofix on save must be enabled. Then you will find that this effect actually depends on count. Autofix will help you automatically fill in the dependencies, and the code looks like this:
UseEffect (() = > {dom.current.addEventListener ('click', () = > setCount (count + 1));}, [count])
Then this is definitely wrong, which is equivalent to re-binding the event every time the count changes. So there are several ways of thinking about event binding, or similar scenarios, and I arrange them according to my regular processing priority:
Idea 1: eliminate dependence
In this scenario, quite simply, we mainly use functional updates, another usage of setCount. Write it this way: () = > setCount (prevCount = > + + prevCount), don't worry about what's new or old, or what closures, so don't worry about it.
Idea 2: rebind events
What if our event is to consume this count? Like this:
Dom.current.addEventListener ('click', () = > {console.log (count); setCount (prevCount = > + + prevCount);})
We don't have to insist on executing it only once in mount. You can also remove events before each re-render, and bind events after render. Here, taking advantage of the features of useEffect, you can read the documents for yourself:
UseEffect (() = > {const $dom = dom.current; const event = () = > {console.log (count); setCount (prev = > + + prev);}; $dom.addEventListener ('click', event); return () = > $dom.removeEventListener (' click', event);}, [count])
Idea 3:
If you find it expensive or troublesome to write, you can also use useRef for its practical useRef. Personally, I don't like this operation, but it can also solve the problem. The code is as follows:
Const [count, setCount] = useState (0); const countRef = useRef (count); useEffect () = > {dom.current.addEventListener ('click', () = > {console.log (countRef.current); setCount (prevCount = > {const newCount = + + prevCount; countRef.current = newCount; return newCount;});});}, []); useCallback and useMemo
These two api, in fact, the concept is very easy to understand, one is the "cache function", the other is the cache "function return value". But we often don't bother to use it, and sometimes even use it incorrectly.
From the above dependency problem, we can actually know that hooks is actually very sensitive to the point of "whether it has changed or not". If an effect uses some data or method internally. If we do not add it to the dependency, it is easy to cause the data or method to be not what we want it to be because of the closure problem. If we add it, it is likely that their changes will lead to the crazy implementation of effect. If you really develop, you should often encounter this kind of problem.
Therefore, I would like to suggest:
Within the component, methods that will become other useEffect dependencies are recommended to be wrapped in useCallback or written directly in the useEffect that references it.
Do not do to others what you do not want to do to others, if your function will be passed to subcomponents as props, be sure to use useCallback package, for subcomponents, if each render will cause you to pass a change in the function, it may cause great trouble. At the same time, it is not conducive to react to do rendering optimization.
However, there is another scenario that is easy to ignore and confuse useCallback with useMemo. The typical scenario is: throttling and anti-shaking.
For example:
Function BadDemo () {const [count, setCount] = useState (1); const handleClick = debounce (()) = > {setCount (c = > + + c);}, 1000); return {count};}
We want to prevent users from continuously clicking to trigger multiple changes, add anti-shake, stop clicking for 1 second before triggering count + 1, this component is ideally OK. But the reality is bony, we have a lot of page components, and this BadDemo may be re-render because of what the parent does. Now if our page were re-render every 500ms, that would be it:
Function BadDemo () {const [count, setCount] = useState (1); const [, setRerender] = useState (false); const handleClick = debounce () = > {setCount (c = > + + c);}, 1000); useEffect () = > {/ / per 500ms, component re-render window.setInterval (() = > {setRerender (r = >! r);}, 500);}, []); return {count};}
Each time render causes handleClick to be a different function, then this anti-shake naturally fails. Such a situation for some particularly high defense requirements of the scene, there is a greater online risk.
What are we going to do? Naturally, I want to add useCallback:
Const handleClick = useCallback (debounce () = > {setCount (c = > + + c);}, 1000), [])
Now we find that the effect meets our expectations, but there is still a big hole hidden behind it.
What if this anti-shake function has some dependencies? Such as setCount (c = > + + c); becomes setCount (count + 1). Then this function relies on count. The code looks like this:
Const handleClick = useCallback (debounce () = > {setCount (count + 1);}, 1000), [])
You will find that your lint rules do not require you to populate the deps array with count as a dependency. This in turn led to the original problem, which was only the first click on count++. Why is that?
Because what is passed into useCallback is an execution statement, not a function declaration. It's just that it executes the new function returned after it is executed, and we use it as the input parameter of the useCallback function, and what exactly this new function is, in fact, we don't know the lint rule.
A more reasonable posture would be to use useMemo:
Const handleClick = useMemo (() = > debounce (() = > {setCount (count + 1);}, 1000), [count])
This ensures that whenever the count changes, a new function with anti-shake function will be returned.
All in all, useMemo is recommended for scenarios that use higher-order functions.
Some netizens have provided valuable feedback, I continue to add: just using useMemo, there are still some problems.
The problem 1:useMemo "future" is not "stable"
React's official documentation mentions: you can use useMemo as a means of performance optimization, but not as a semantic guarantee. In the future, React may choose to "forget" some previous memoized values and recalculate them the next time you render, such as freeing memory for off-screen components. First write code that can be executed without > useMemo-then add > useMemo to your code to optimize performance. In other words, in some special case in the future, the anti-shake function will still fail. Of course, this situation occurs in the "future", and it is relatively extreme, and the probability of occurrence is relatively small. Even if it occurs, it will not occur continuously within a short period of time. So for scenarios that are not "the front end is going to die", the risk is relatively small.
The problem 2:useMemo can't solve all high-order function scenarios once and for all.
In the example scenario, the anti-shake logic is: "one second after a continuous click, the logic is actually executed, and the repeated clicks in the process are invalid." If the business logic is changed to "status changes immediately after clicking, and then repeated clicks are invalid within 1 second", then our code may become.
Const handleClick = useMemo (() = > throttle (() = > {setCount (count + 1);}, 1000), [count])
And then it didn't work again. The reason is that immediately after clicking, the count changes, and then handleClick repeatedly generates a new function, and this throttling becomes invalid.
So in this scenario, the idea goes back to "eliminating dependencies" or "using ref" as mentioned earlier.
Of course, you can also choose to implement a debounce or throttle manually. I suggest you directly use the community libraries, such as react-use, or write your own two implementations with reference to their implementations.
The above is about the content of the article "react hooks online post-bug analysis". I believe we all have a certain understanding. I hope the content shared by the editor will be helpful to you. If you want to know more about the relevant knowledge, please follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.