Author: Shadeed
Translator: front end little wise
Source: dmitripavlutin
Have a dream, have dry goods, WeChat search [big move world] keep an eye on this washing dishes in the wee hours of the morning.
This paper making https://github.com/qq449245884/xiaozhi has included, has a complete line companies interview examination site, information, and my series of articles.
UseEffect () is used primarily to manage side effects, such as fetching over the network, manipulating the DOM directly, and starting and ending timers.
Although useEffect() and useState(a method of managing state) are among the most commonly used hooks, they take some time to become familiar with and use correctly.
One pitfall you might run into with useEffect() is the infinite loop of component rendering. In this article, I’ll look at common scenarios that create infinite loops and how to avoid them.
1. Infinite loop and side effects update status
Suppose we have a function component that has an input element in it. The function of the component is to count the number of changes to the input.
We’ll call this component CountInputChanges and it will look something like this:
function CountInputChanges() {
const [value, setValue] = useState('');
const [count, setCount] = useState(-1);
useEffect(() => setCount(count + 1));
const onChange = ({ target }) => setValue(target.value);
return (
<div>
<input type="text" value={value} onChange={onChange} />
<div>Number of changes: {count}</div>
</div>
)
}
The value variable holds the value of the input input, and the onChange event handler updates the value state as the user enters the input.
Here, use useEffect() to update the count variable. UseEffect (() => setCount(Count + 1)) updates the counter each time a component is rendered due to user input.
Because useEffect(() => setCount(Count + 1)) is used without dependent parameters, ()=> setCount(Count + 1) performs a callback after each component is rendered.
Do you think this is a problem? Open the demonstration oneself try: https://codesandbox.io/s/infi…
The count state variable increases uncontrollably, even if nothing is entered in the input. This is an infinite loop.
The problem is the way useEffect() is used:
useEffect(() => setCount(count + 1));
It generates an infinite loop of components to re-render.
After the initial rendering, useEffect() executes the side effect callback function that updates the state. The status update triggers a rerender. After the rerender, useEffect() executes the side effect callback and updates the state again, which triggers the rerender again.
1.1 Resolve through dependency
Infinite loops can be fixed by properly managing the useEffect(Callback, Dependencies) dependency parameters.
Because we want the count to increase as the value changes, we can simply use the value as a dependency on the side effect.
import { useEffect, useState } from 'react';
function CountInputChanges() {
const [value, setValue] = useState('');
const [count, setCount] = useState(-1);
useEffect(() => setCount(count + 1), [value]);
const onChange = ({ target }) => setValue(target.value);
return (
<div>
<input type="text" value={value} onChange={onChange} />
<div>Number of changes: {count}</div>
</div>
);
}
Add [value] as a dependency on useEffect so that the count state variable is updated only when [value] changes. Doing so solves the infinite loop.
1.2 use the ref
In addition to dependency, we can solve this problem with userRef ().
The idea is that updating the Ref does not trigger a rerender of the component.
import { useEffect, useState, useRef } from "react";
function CountInputChanges() {
const [value, setValue] = useState("");
const countRef = useRef(0);
useEffect(() => countRef.current++);
const onChange = ({ target }) => setValue(target.value);
return (
<div>
<input type="text" value={value} onChange={onChange} />
<div>Number of changes: {countRef.current}</div>
</div>
);
}
UseEffect (() => CountRef.current+ +) CountRef.current+ + is returned each time the value is rerendered due to a change. A reference change by itself does not trigger a component rerender.
2. Infinite loops and new object references
Even if the useEffect() dependency is set up correctly, be careful when using objects as dependencies.
For example, the following component CountSecrets listens for words that the user enters in the input, and once the user enters the special word ‘secret’, the count of ‘secret’ is increased by one.
import { useEffect, useState } from "react"; function CountSecrets() { const [secret, setSecret] = useState({ value: "", countSecrets: 0 }); useEffect(() => { if (secret.value === 'secret') { setSecret(s => ({... s, countSecrets: s.countSecrets + 1})); } }, [secret]); const onChange = ({ target }) => { setSecret(s => ({ ... s, value: target.value })); }; return ( <div> <input type="text" value={secret.value} onChange={onChange} /> <div>Number of secrets: {secret.countSecrets}</div> </div> ); }
Open the demo (https://codesandbox.io/s/infi.) Try it out for yourself. Now type secret, and the value of secret. Countsecrets starts to grow out of control.
This is an infinite loop problem.
Why is that?
The secret object is used as a useEffect(… , secret). In the side effect callback function, the update function is called as long as the input value is equal to secret
setSecret(s => ({... s, countSecrets: s.countSecrets + 1}));
This increases the value of CountSecrets, but also creates a new object.
Secret is now a new object, and the dependencies have changed. So useEffect (… , [secret]) and the side effects of calling again to update the state and creating a new secret object again, and so on.
Two objects in JavaScript are equal only if they refer to exactly the same object.
2.1 Avoid objects as dependencies
The best way to solve the infinite loop problem caused by looping to create new objects is to avoid using object references in the dependencies parameter of useEffect().
let count = 0;
useEffect(() => {
// some logic
}, [count]); // Good!
let myObject = {
prop: 'Value'
};
useEffect(() => {
// some logic
}, [myObject]); // Not good!
useEffect(() => {
// some logic
}, [myObject.prop]); // Good!
Fixed
component infinite loop issue, can use the UseEffect (… , [secret])) to useEffect(… Value], [secret.).
It is sufficient to call the side effect callback only when the secret.value changes. Here is the fixed code:
import { useEffect, useState } from "react"; function CountSecrets() { const [secret, setSecret] = useState({ value: "", countSecrets: 0 }); useEffect(() => { if (secret.value === 'secret') { setSecret(s => ({... s, countSecrets: s.countSecrets + 1})); } }, [secret.value]); const onChange = ({ target }) => { setSecret(s => ({ ... s, value: target.value })); }; return ( <div> <input type="text" value={secret.value} onChange={onChange} /> <div>Number of secrets: {secret.countSecrets}</div> </div> ); }
3 summary
UseEffect (callback, deps) is a Hook that executes a callback(side effect) after the component is rendered. If you do not pay attention to side effects, you may trigger an infinite loop of component rendering.
A common case for generating an infinite loop is to update the state in the side effect without specifying any dependent parameters
useEffect(() => {
// Infinite loop!
setState(count + 1);
});
An effective way to avoid an infinite loop is to set the dependencies correctly:
useEffect(() => {
// No infinite loop
setState(count + 1);
}, [whenToUpdateValue]);
Alternatively, you can use Ref, and updating Ref does not trigger a rerender:
useEffect(() => {
// No infinite loop
countRef.current++;
});
Another common approach to infinite loops is to use an object as a dependency on useEffect() and update that object in the side effect (effectively creating a new object)
useEffect(() => { // Infinite loop! setObject({ ... object, prop: 'newValue' }) }, [object]);
Avoid using objects as dependencies and only use specific attributes (the end result should be a raw value) :
useEffect(() => { // No infinite loop setObject({ ... object, prop: 'newValue' }) }, [object.whenToUpdateProp]);
When using useEffect(), are there any other ways you know of causing an infinite loop trap?
After, I am small wisdom, we will see you next time ~
After the deployment of the code may exist bugs can not be known in real time, in order to solve these bugs afterwards, spent a lot of time to debug the log, here by the way to recommend a good BUG monitoring toolFundebug.
Original: https://dmitripavlutin.com/re…
communication
Have a dream, have dry goods, WeChat search [big move world] keep an eye on this washing dishes in the wee hours of the morning.
In this paper, making https://github.com/qq44924588… Has been included, a line of big factory interview complete test site, data and my series of articles.