问题描述
根据react文档,useEffect
会在重新运行useEffect
部分之前触发清理逻辑.
According to react document, useEffect
will trigger clean-up logic before it re-runs useEffect
part.
如果你的效果返回一个函数,React 会在需要清理的时候运行它...
If your effect returns a function, React will run it when it is time to clean up...
没有用于处理更新的特殊代码,因为 useEffect
默认处理它们.它会在应用下一个效果之前清理之前的效果...
There is no special code for handling updates because useEffect
handles them by default. It cleans up the previous effects before applying the next effects...
但是,当我在 useEffect
中使用 requestAnimationFrame
和 cancelAnimationFrame
时,我发现 cancelAnimationFrame 可能无法正常停止动画.有时,我发现旧动画仍然存在,而下一个效果带来另一个动画,这导致我的 Web 应用程序性能问题(尤其是当我需要渲染大量 DOM 元素时).
However, when I use requestAnimationFrame
and cancelAnimationFrame
inside useEffect
, I found the cancelAnimationFrame may not stop the animation normally. Sometimes, I found the old animation still exists, while the next effect brings another animation, which causes my web app performance issues (especially when I need to render heavy DOM elements).
我不知道 react 钩子在执行清理代码之前是否会做一些额外的事情,这会使我的取消动画部分无法正常工作,useEffect
钩子会做一些类似于闭包的事情锁定状态变量?
I don't know whether react hook will do some extra things before it executes the clean-up code, which make my cancel-animation part not work well, will useEffect
hook do something like closure to lock the state variable?
useEffect 的执行顺序和内部清理逻辑是什么?是不是我下面写的代码有问题,导致cancelAnimationFrame不能正常工作?
What's useEffect's execution order and its internal clean-up logic? Is there something wrong the code I write below, which makes cancelAnimationFrame can't work perfectly?
谢谢.
//import React, { useState, useEffect } from "react";
const {useState, useEffect} = React;
//import ReactDOM from "react-dom";
function App() {
const [startSeconds, setStartSeconds] = useState(Math.random());
const [progress, setProgress] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setStartSeconds(Math.random());
}, 1000);
return () => clearInterval(interval);
}, []);
useEffect(
() => {
let raf = null;
const onFrame = () => {
const currentProgress = startSeconds / 120.0;
setProgress(Math.random());
// console.log(currentProgress);
loopRaf();
if (currentProgress > 100) {
stopRaf();
}
};
const loopRaf = () => {
raf = window.requestAnimationFrame(onFrame);
// console.log('Assigned Raf ID: ', raf);
};
const stopRaf = () => {
console.log("stopped", raf);
window.cancelAnimationFrame(raf);
};
loopRaf();
return () => {
console.log("Cleaned Raf ID: ", raf);
// console.log('init', raf);
// setTimeout(() => console.log("500ms later", raf), 500);
// setTimeout(()=> console.log('5s later', raf), 5000);
stopRaf();
};
},
[startSeconds]
);
let t = [];
for (let i = 0; i < 1000; i++) {
t.push(i);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<text>{progress}</text>
{t.map(e => (
<span>{progress}</span>
))}
</div>
);
}
ReactDOM.render(<App />,
document.querySelector("#root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
推荐答案
将这三行代码放在一个组件中,你会看到它们的优先顺序.
Put these three lines of code in a component and you'll see their order of priority.
useEffect(() => {
console.log('useEffect')
return () => {
console.log('useEffect cleanup')
}
})
window.requestAnimationFrame(() => console.log('requestAnimationFrame'))
useLayoutEffect(() => {
console.log('useLayoutEffect')
return () => {
console.log('useLayoutEffect cleanup')
}
})
useLayoutEffect >requestAnimationFrame >使用效果
您遇到的问题是由 loopRaf
在执行 useEffect
的清理功能之前请求另一个动画帧引起的.
The problem you're experiencing is caused by loopRaf
requesting another animation frame before the cleanup function for useEffect
is executed.
进一步的测试表明,useLayoutEffect
总是在 requestAnimationFrame
之前调用,并且它的清理函数在下次执行之前调用,以防止重叠.
Further testing has shown that useLayoutEffect
is always called before requestAnimationFrame
and that its cleanup function is called before the next execution preventing overlaps.
将 useEffect
更改为 useLayoutEffect
它应该可以解决您的问题问题.
Change
useEffect
touseLayoutEffect
and it should solve your problem.
useEffect
和 useLayoutEffect
是按照它们在代码中出现的顺序被调用的,就像 useState
调用一样.
useEffect
and useLayoutEffect
are called in the order they appear in your code for like types just like useState
calls.
您可以通过运行以下几行来看到这一点:
You can see this by running the following lines:
useEffect(() => {
console.log('useEffect-1')
})
useEffect(() => {
console.log('useEffect-2')
})
useLayoutEffect(() => {
console.log('useLayoutEffect-1')
})
useLayoutEffect(() => {
console.log('useLayoutEffect-2')
})
这篇关于react 钩子中的 useEffect 执行顺序及其内部清理逻辑是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!