gpt4 book ai didi

javascript - 使用requestAnimationFrame和cancelAnimationFrame时useEffect的执行顺序和内部清理逻辑是什么?

转载 作者:数据小太阳 更新时间:2023-10-29 03:51:00 24 4
gpt4 key购买 nike

根据 React 文档,useEffect 将在重新运行 useEffect 部分之前触发清理逻辑。

If your effect returns a function, React will run it when it is time to clean up...

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 中使用 requestAnimationFramecancelAnimationFrame 时,我发现 cancelAnimationFrame 可能无法正常停止动画。有时,我发现旧动画仍然存在,而下一个效果会带来另一个动画,这会导致我的网络应用程序出现性能问题(尤其是当我需要渲染大量 DOM 元素时)。

我不知道 react hook 是否会在执行清理代码之前做一些额外的事情,这使得我的取消动画部分不能正常工作,useEffect hook 会做一些类似闭包的事情吗锁定状态变量?

useEffect 的执行顺序和内部清理逻辑是什么?是不是我下面写的代码有问题,导致cancelAnimationFrame不能完美运行?

谢谢。

//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>

最佳答案

上述答案中有一点不清楚,那就是混音中有多个组件时效果运行的顺序。我们一直在做的工作涉及通过 useContext 在 parent 和 child 之间进行协调,因此顺序对我们来说更重要。 useLayoutEffectuseEffect 在这方面的工作方式不同。

useEffect 在移动到下一个组件(深度优先)并执行相同操作之前运行清理和新效果。

useLayoutEffect 运行每个组件的清理(深度优先),然后运行所有组件的新效果(深度优先)。

render parent
render a
render b
layout cleanup a
layout cleanup b
layout cleanup parent
layout effect a
layout effect b
layout effect parent
effect cleanup a
effect a
effect cleanup b
effect b
effect cleanup parent
effect parent
const Test = (props) => {
const [s, setS] = useState(1)

console.log(`render ${props.name}`)

useEffect(() => {
const name = props.name
console.log(`effect ${props.name}`)
return () => console.log(`effect cleanup ${name}`)
})

useLayoutEffect(() => {
const name = props.name
console.log(`layout effect ${props.name}`)
return () => console.log(`layout cleanup ${name}`)
})

return (
<>
<button onClick={() => setS(s+1)}>update {s}</button>
<Child name="a" />
<Child name="b" />
</>
)
}

const Child = (props) => {
console.log(`render ${props.name}`)

useEffect(() => {
const name = props.name
console.log(`effect ${props.name}`)
return () => console.log(`effect cleanup ${name}`)
})

useLayoutEffect(() => {
const name = props.name
console.log(`layout effect ${props.name}`)
return () => console.log(`layout cleanup ${name}`)
})

return <></>
}

关于javascript - 使用requestAnimationFrame和cancelAnimationFrame时useEffect的执行顺序和内部清理逻辑是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53781632/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com