Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's useEffect execution order and its internal clean-up logic in react hooks?

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>
like image 517
hijiangtao Avatar asked Dec 14 '18 14:12

hijiangtao


People also ask

What is the execution order of useEffect?

useEffect orderEverything outside the effects will run first, and then the effects will be called in order. Notice that useEffect accepts a dependency array, which will trigger the effect when the component first mounts and when any of the dependencies change.

What is a useEffect clean up function?

What is the useEffect cleanup function? Just like the name implies, the useEffect cleanup is a function in the useEffect Hook that allows us to tidy up our code before our component unmounts. When our code runs and reruns for every render, useEffect also cleans up after itself using the cleanup function.

What is useEffect in React hooks?

The useEffect Hook allows you to perform side effects in your components. Some examples of side effects are: fetching data, directly updating the DOM, and timers. useEffect accepts two arguments. The second argument is optional.

How do you use cleanup in useEffect in React?

The hook comes with a cleanup function, which you might not always need, but it can come in handy. To invoke the cleanup function you can simply add a return function like so: useEffect(() => { // Your effect return () => { // Cleanup }; }, []); The cleanup can prevent memory leaks and remove unwanted things.


1 Answers

One thing that's not clear in the above answers is the order in which the effects run when you have multiple components in the mix. We've been doing work that involves coordination between a parent and it's children via useContext so the order matters more to us. useLayoutEffect and useEffect work in different ways in this regard.

useEffect runs the clean up and the new effect before moving to the next component (depth first) and doing the same.

useLayoutEffect runs the clean ups of each component (depth first), then runs the new effects of all components (depth first).

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 <></> } 
like image 186
Aidan Kane Avatar answered Sep 28 '22 04:09

Aidan Kane