Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What errors should be avoided when using React Hooks

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

Editor to share with you what mistakes to avoid when using React Hooks, I believe most people do not know much about it, so share this article for your reference, I hope you can learn a lot after reading this article, let's go to know it!

This paper mainly introduces the misuse of React hooks and how to solve them.

Do not change the order of Hook calls

Do not use obsolete status

Do not create outdated closures

Do not use state for infrastructure data

Don't forget to clean up the side effects.

1. Do not change the order of Hook calls

A few days before I wrote this article, I wrote a component that gets game information through id. Here is a simple version of FetchGame:

Function FetchGame ({id}) {if (! id) {return 'Please select a game to fetch';} const [game, setGame] = useState ({name:', description:''}); useEffect () = > {const fetchGame = async () = > {const response = await fetch (`/ api/game/$ {id}`); const fetchedGame = await response.json (); setGame (fetchedGame);} FetchGame ();}, [id]); return (Name: {game.name} Description: {game.description})

The component FetchGame receives the id (that is, the ID of the game to be obtained). UseEffect () extracts the game information in await fetch (/ game/$ {id}) and saves it to the state variable game.

Open the demo (https://codesandbox.io/s/hooks-order-warning-rdxpg?file=/pages/index.js). The component correctly performs the fetch operation and updates the status with the acquired data. But take a look at the tab Eslint warning: there is a problem with the incorrect order of Hook execution.

The problem lies in this judgment:

Function FetchGame ({id}) {if (! id) {return 'Please select a game to fetch';} / /.}

When id is empty, the component renders' Please select a game to fetch' and exits without calling any Hook.

However, if id is not empty (for example, equal to'1'), useState () and useEffect () are called.

Conditional execution of Hook can lead to unexpected errors that are difficult to debug. The internal way React Hook works requires that components always call Hook in the same order between renderings.

This is the first rule of hooks: do not call Hook within loops, conditions, or nested functions.

The solution is to put the conditional judgment after Hook:

Function FetchGame ({id}) {const [game, setGame] = useState ({name:'', description:''}); useEffect () = > {const fetchGame = async () = > {const response = await fetch (`/ api/game/$ {id}`); const fetchedGame = await response.json (); setGame (fetchedGame);}; if (id) {fetchGame () }, [id]); if (! id) {return 'Please select a game to fetch';} return (Name: {game.name} Description: {game.description});}

Now, whether id is empty or not, useState () and useEffect () are always called in the same order, which is how Hook should always be called.

two。 Do not use obsolete status

The following component MyIncreaser adds the state variable count when the button is clicked:

Function MyIncreaser () {const [count, setCount] = useState (0); const increase = useCallback (() = > {setCount (count + 1);}, [count]); const handleClick = () {increase ();}; return (Increase Counter: {count});}

What's interesting here is that handleClick invokes three status updates.

Now, before opening the demo, ask a question: if you click the button once, will the counter increase by 3?

Open the demo (https://codesandbox.io/s/stale-variable-jo32q?file=/src/index.js)) and click the button once to see the result.

Sorry, even if increase () is called three times in handleClick (), the count only increases by 1.

The problem is the setCount (count + 1) state updater. When the button is clicked, React calls setCount (count + 1) 3 times

Const handleClick = () {increase (); / equivalent: const handleClick = () {setCount (count + 1); / / count variable is now stale setCount (count + 1); setCount (count + 1);}

The first call to setCount (count + 1) correctly updates the counter to count + 1 = 0 + 1 = 1. However, the next two setCount (count + 1) calls also set the count to 1 because they use the outdated stale state.

Resolve obsolete states by updating states functionally. We use setCount (count = > count + 1) instead of setCount (count + 1):

Function MyIncreaser () {const [count, setCount] = useState (0); const increase = useCallback (() = > {setCount (count = > count + 1);}, []); const handleClick = () {increase ();}; return (Increase Counter: {count});}

Here is a good rule to avoid encountering outdated variables:

If you use the current state to calculate the next state, always update the state with a function: setValue (prevValue = > prevValue + someResult).

3. Do not create outdated closures

React Hook is programmatically dependent on the concept of closures. Relying on closures is why they are so expressive.

Closures in JavaScript are functions that capture variables from their lexical scope. No matter where the closure is executed, it can always access the variable from the place where it is defined.

When using Hook to accept callbacks as parameters (such as useEffect (callback, deps), useCallback (callback, deps), you may create an obsolete closure, a closure that captures outdated states or variables.

Let's take a look at an example of an obsolete closure created when you use useEffect (callback, deps) and forget to set the dependency correctly.

In the component, useEffect () prints the value of count every 2 seconds

Const [count, setCount] = useState (0); useEffect (function () {setInterval (function log () {console.log (`Count is: ${count} `);}, 2000);}, []); const handleClick = () = > setCount (count = > count + 1); return (Increase Counter: {count});}

Open the demo (https://codesandbox.io/s/stale-variable-jo32q?file=/src/index.js)) and click the button. In the console, Count is: 0J is printed every 2 seconds, no matter what the actual value of the count state variable is.

Why is it like this?

The first time you render, the value of count captured by the log function is 0.

After that, when the button is clicked and the count increases, the count value taken by setInterval is still the value of 0 captured from the initial rendering. The log function is an outdated closure because it captures an obsolete state variable count.

The solution is to let useEffect () know that the closure log depends on count and reset the timer correctly

Function WatchCount () {const [count, setCount] = useState (0); useEffect (function () {const id = setInterval (function log () {console.log (`Count is: ${count} `);}, 2000); return () = > clearInterval (id);}, [count]); const handleClick = () = > setCount (count = > count + 1); return (Increase Counter: {count});}

Once the dependency is set correctly, useEffect () updates the closure of setInterval () as soon as the count changes.

To prevent closures from capturing old values: make sure that dependencies are used in the callback function provided to Hook.

4. Do not use state for infrastructure data

Once, I need to call side effects on status updates, without calling side effects on the first rendering. UseEffect (callback, deps) always calls the callback function after the component is mounted: so I want to avoid this.

I found the following solution

Function MyComponent () {const [isFirst, setIsFirst] = useState (true); const [count, setCount] = useState (0); useEffect () = > {if (isFirst) {setIsFirst (false); return;} console.log ('The counter increments');}, [count]); return (setCount (count = > count + 1)} > Increase);}

The state variable isFirst is used to determine whether it is rendered for the first time. Once the setIsFirst (false) is updated, another re-rendering occurs for no reason.

It makes sense to maintain the count state because the interface needs to render the value of count. However, isFirst cannot be used directly to calculate the output.

Information about whether or not to render for the first should not be stored in this state. Infrastructure data, such as details about rendering cycles (that is, first rendering, number of renderings), timer ID (setTimeout (), setInterval ()), and direct references to DOM elements, should be stored and updated with references to useRef ().

We store the information about the first rendering in Ref:

Const isFirstRef = useRef (true); const [count, setCount] = useState (0); useEffect () = > {if (isFirstRef.current) {isFirstRef.current = false; return;} console.log ('The counter increments');}, [count]); return (count = > count + 1)} > Increase)

IsFirstRef is a reference that holds information about whether it is the first rendering of a component. The isFirstRef.current property is used to access and update the referenced value.

Important: updating the reference isFirstRef.current = false does not trigger re-rendering.

5. Don't forget to clean up the side effects.

Many side effects, such as getting a request or using a timer like setTimeout (), are asynchronous.

If the component is uninstalled or the result of the side effect is no longer needed, don't forget to clean up the side effect.

The following component has a button. When the button is clicked, the counter increases by 1 per second delay:

Function DelayedIncreaser () {const [count, setCount] = useState (0); const [increase, setShouldIncrease] = useState (false); useEffect () = > {if (increase) {setInterval (()) = > {setCount (count = > count + 1)}, 1000);}}, [increase]); return (setShouldIncrease (true)} > Start increasing Count: {count});}

Open the demo (https://codesandbox.io/s/unmounted-state-update-n1d3u?file=/src/index.js)) and click the start button. As expected, the state variable count increases every second.

When incrementing, click the umount button to uninstall the component. React warns you in the console to update the status of the uninstalled component.

Fixing DelayedIncreaser is simple: simply return the cleanup function from the callback of useEffect ():

/ /... UseEffect (() = > {if (increase) {const id = setInterval (()) = > {setCount (count = > count + 1)}, 1000); return () = > clearInterval (id);}, [increase]); / /.

That is, every time you write side-effect code, ask yourself if it should be cleaned up. Timers, frequent requests (such as uploading files), and sockets almost always need to be cleaned.

6. Summary

The best way to start with React hooks is to learn how to use them.

But you can also encounter situations where you can't understand why their behavior is different from what you expected. It's not enough to know how to use React Hook: you should also know when not to use them.

The first thing not to do is to conditionally render Hook or change the order of Hook calls. Regardless of the Props or status value, React expects components to always call Hook in the same order.

The second thing to avoid is to use obsolete state values. To avoid obsolete status, update the status functionally.

Don't forget to point out the dependencies of Hook that take callback functions as arguments: for example, useEffect (callback, deps), useCallback (callback, deps), which solves the problem of obsolete closures.

Do not store infrastructure data (such as setTimeout () or setInterval ()) in the state about component rendering cycles. The rule of thumb is to keep this kind of data in Ref.

The above is all the contents of the article "what mistakes to avoid when using React Hooks". Thank you for reading! I believe we all have a certain understanding, hope to share the content to help you, if you want to learn more knowledge, welcome to 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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report