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
I’ve spent the night looking into this problem. Check out this code:
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.
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)
toconsole.log(thing.innerHTML)
and you can see the latest updatedinnerHTML
property. Alternatively you could pass thediv.innerHTML
totellMeWhatsPassed
This isn’t exactly an answer but I hope my findings at least help you!
You are right… It will delay the execution till the executing code must complete.
Putting some delay for witnessing UI changes is helpful.