Original: https://juejin.cn/post/6972439516703358990
Author: Tonychen
Infinite Chain Of Update
In actual use, sometimes you will encounter Infinite Chain Of Update
, which is actually a piece of your code that caused an "infinite loop update". Let's take a look at a few examples 👇
Dependent array problem
For example, when using useEffect
, the dependency array is not passed in 👇
// count 会无限 + 1
function App() {
const [count, setCount] = useState(0)
useEffect(() => {
setCount(count + 1)
})
}
Why is count
updated indefinitely? The logic here is like this 👇
- Component update
- Execute
useEffect
- Update
count
and trigger component update - Execute
useEffect
- ……
The solution is very simple. Just useEffect
as the third parameter, and useEffect
will not be executed during the next update.
// 正常渲染
function App() {
const [count, setCount] = useState(0)
useEffect(() => {
setCount(count + 1)
}, [])
}
The updated value is monitored
This is a problem that novice hooks
players often encounter, and veterans also have some headaches.
- Case 1
useEeffect
updated state
indirectly affects the monitored variables, for example 🌰
function App() {
const [obj, setObj] = useState({a: 0})
const {a} = obj
useEffect(() => {
setObj({
...obj,
a: 1
})
}, [a, obj])
}
The above code will cause an infinite loop when it is actually running. Why? Because the setObj
was changed at obj
, and useEffect
monitored this value, which resulted in an endless loop...
How to solve it? Since obj
is caused by the change of infinite loop
, in fact, as long as obj
is not monitored, there is no such thing.
🤪, where you can make use of setState
"callback" usage 👇
function App() {
const [obj, setObj] = useState({a: 0})
const {a} = obj;
useEffect(() => {
setObj((state) => ({
...state,
a: 1
}))
}, [a])
}
- Case 2
Sometimes you need to decide what the component displays based on different "states", then you usually need to use a state
to control the display of several "states". The transition from state 1 to state 2 is asynchronous. A simple way is to use useEffect
to monitor it.
If one part of this state depends on external input, the other part is processed according to the state change of this external input. Give an example 👇
export function App({outsider, wait}) {
const [state, setState] = useState('INIT')
useEffect(() => {
// 根据 ousider 处理 state 的值
if (outsider === true) {
setState('PENDING')
} else {
if (state === 'PENDING') {
setTimeout(() => {
setState('RESOLVED')
}, wait)
}
}
}, [outsider, state])
return (
// 根据 state 来渲染不同的组件/样式
)
}
In actual operation, it is infinite loop
again. Maybe you have the same idea as I thought the first time, which is to use the solution of "Case 1". But note that there is asynchronous processing here, so here can only be said to use useRef
to do a simple processing.
export function App({outsider, wait}) {
const [state, setState] = useState('INIT')
const stateRef = useRef(state)
useEffect(() => {
// 根据 ousider 处理 state 的值
if (outsider === true) {
setState('PENDING')
stateRef.current = 'PENDING'
} else {
if (stateRef.current === 'PENDING') {
setTimeout(() => {
setState('RESOLVED')
stateRef.current = 'RESOLVED'
}, wait)
}
}
}, [outsider])
return (
// 根据 state 来渲染不同的组件/样式
)
}
Thus in useEffect
you do not need to rely on the state
, but also according to state
make some operating current value 😄
summary
When writing hooks
, you need to always pay attention to whether there is a dependency on state
and setState
in the code. Usually, the intuitive way of writing will bring infinite loop
.
Can't get the latest value
Newbies hooks
often encounter this kind of problem, here is a simple example👇
import { useCallback, useEffect, useState } from "react";
let timeout = null;
export default function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
timeout = setTimeout(() => {
console.log("timeout", count);
setCount(count + 1);
}, 1000);
}, [count]);
useEffect(() => {
return () => {
clearTimeout(timeout);
};
}, []);
return (
<div className="App">
<p>{count}</p>
<button onClick={handleClick}>click me</button>
</div>
);
}
After running, you will find that every time console.log
prints count + 1
, and this is actually useState
implementation of 060c5ad018a6ab, here only a small part of the implementation in the source code is intercepted👇
It can be seen from useState
deconstruction is out of the value of the original data rather than references, so in the example above, the setTimeout
get the latest in count
value.
Reference
Setting State based on Previous State in useEffect - Its a trap!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。