Monday, June 3, 2024
 Popular · Latest · Hot · Upcoming
61
rated 0 times [  62] [ 1]  / answers: 1 / hits: 54919  / 6 Years ago, fri, december 14, 2018, 12:00:00

According to react document, useEffect will trigger clean-up logic before it re-runs useEffect part.



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



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).


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?


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?


Thanks.




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




More From » reactjs

 Answers
11

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



The problem you're experiencing is caused by loopRaf requesting another animation frame before the cleanup function for useEffect is executed.



Further testing has shown that useLayoutEffect is always called before requestAnimationFrame and that its cleanup function is called before the next execution preventing overlaps.




Change useEffect to useLayoutEffect and it should solve your
problem.




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')
})

[#52919] Monday, December 10, 2018, 6 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
josefn

Total Points: 251
Total Questions: 93
Total Answers: 84

Location: Senegal
Member since Fri, Aug 21, 2020
4 Years ago
;