skip to Main Content

From the website and some articles, i have learned that any changes made to the app will first be reflected in the Virtual DOM. and then to the Real DOM after diffing both trees. the lifecycle hook useEffect will fire once the Real DOM is populated.

Problem

import React from 'react';
import { useEffect, useRef } from 'react';

export function App(props) {
  const ref = useRef();
  useEffect(() => {


    const ref2 = ref.current;

    const div = document.createElement('div')
    div.innerHTML = "ASDF"
    div.style.position = "absolute";
    div.style.top = 0;
    div.style.transition = "all 5s"


    ref2.appendChild(div);

    // this should work theoretically and my div should get a transition
    div.style.top = "100px"


  }, [])
  console.log("render")
  return (
    <div ref={ref} className='App'>
      <h1>Hello React.</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

// Log to console
console.log('Hello console')

Running the above code gives a static div with top as 100px instead of a transition.

So, I tried the following code

import React from 'react';
import { useEffect, useRef } from 'react';

export function App(props) {
  const ref = useRef();
  useEffect(() => {


    const ref2 = ref.current;

    const div = document.createElement('div')
    div.innerHTML = "ASDF"
    div.style.position = "absolute";
    div.style.top = 0;
    div.style.transition = "all 5s"



    ref2.appendChild(div);
    
    // The following three lines is the only change
    setTimeout(()=>{
      div.style.top = "100px"
    }, 0)

  }, [])
  console.log("render")
  return (
    <div ref={ref} className='App'>
      <h1>Hello React.</h1>
      <h2>Start editing to see some magic happen!</h2>
    </div>
  );
}

// Log to console
console.log('Hello console')

and this gives me the result i wanted. where initially the div is positioned at the top and animates to 100px.

**Why does a 0 second setTimeout work? Does it have something to do with the event loop? **

I can’t use useTransition, animations API, framer-motion or any other animation method. I need the transition to happen this way only. Thank you for your patience!

2

Answers


  1. I’ve spent the night looking into this problem. Check out this code:

    function tellMeWhatsPassed(thing){
       console.log(thing)
    }
    
    function createThing(){
       const div = {}
       div.innerHTML = 'original'
       tellMeWhatsPassed(div)
       div.innerHTML = 'updated'
       tellMeWhatsPassed(div)
    }
    
    createThing()
    
    // {innerHTML: 'original'}
    // {innerHTML: 'updated'}
    
    

    Since JS executes sequentially we expect that since we are calling the tellMeWhatsPassed with two different arguments we get two different console logs.

    Now watch what happens when we update the code and instead of creating an object we create a html div element.

    function tellMeWhatsPassed(thing){
       console.log(thing)
    }
    
    function createThing(){
       const div = document.createElement('div')
       div.innerHTML = 'original'
       tellMeWhatsPassed(div)
       div.innerHTML = 'updated'
       tellMeWhatsPassed(div)
    }
    
    createThing()
    
    // <div> updated </div>
    // <div> updated </div>
    
    

    We’ve got two things going on: the DOM API is being executed off the main stack and is reintroduced through the event loop AND there’s a quirk with console logging that shows us only the latest when passing a reference compared to a primitive.

    To prove this last point convert the console.log(thing) to console.log(thing.innerHTML) and you can see the latest updated innerHTML property. Alternatively you could pass the div.innerHTML to tellMeWhatsPassed

    This isn’t exactly an answer but I hope my findings at least help you!

    Login or Signup to reply.
  2. You are right… It will delay the execution till the executing code must complete.
    Putting some delay for witnessing UI changes is helpful.

    This is because even though setTimeout was called with a delay of zero, it’s placed on a queue and scheduled to run at the next opportunity; not immediately. Currently-executing code must complete before functions on the queue are executed, thus the resulting execution order may not be as expected.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search