Here is my styled components version
"@types/styled-components": "^5.1.26"
and I'll give you my example code.
// index.tsx
import React, { useEffect, useState } from 'react';
import { ProfileHomeContainer } from './styled';
const ProfileHome: React.FC = () => {
const [test, setTest] = useState(false);
useEffect(() => {
const my = document.getElementById('test');
console.log(my);
console.log(test);
setTest(true);
}, [test]);
return (
<ProfileHomeContainer id='test' test={test ? 1 : 0}>
</ProfileHomeContainer>
)
}
// styled.ts
import styled from 'styled-components';
interface ProfileHomeContainerProps {
test: number;
}
const ProfileHomeContainer = styled.div<ProfileHomeContainerProps>`
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background: rgba(0, 0, 0, 0.9);
z-index: 1101;
opacity: ${(props) => (props.test ? '1' : '0')};
transition: 1s;
`;
and There are two Global CSS classes for ProfileHomeContainer.
// which has opacity: 0
.KBlzF{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;position:fixed;top:0;left:0;width:100%;height:100vh;background:rgba(0,0,0,0.9);z-index:1101;opacity:0;-webkit-transition:1s;transition:1s;}
// has opacity: 1
.goFgwQ{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;position:fixed;top:0;left:0;width:100%;height:100vh;background:rgba(0,0,0,0.9);z-index:1101;opacity:1;-webkit-transition:1s;transition:1s;}
and here is my console logs
<div id="test" class="sc-crXcEl goFgwQ"> // result of console.log(my)
false // result of console.log(test)
<div id="test" class="sc-crXcEl goFgwQ">
true
Here is my question. Why my component rendered with goFgwQ first? I think it should be like this.
<div id="test" class="sc-crXcEl KBlzF">
false
<div id="test" class="sc-crXcEl goFgwQ">
true
Because, when useEffect run first time, I have false
state value for test.
Here is my real code in github
This is an "artifact" due to the console.log
slight delay, and depending on its implementation, also worsened by its lazy access.
As you probably noticed, your transition
does work, which means that your styled div (<ProfileHomeContainer>
) did render first as opacity 0, then after at least an animation frame, was modified by the state change to get the other class version with opacity 1 (as expected from your useEffect
).
The counter-intuitive part is that these 2 steps are not reflected by the console.log(element)
, which display only the latter class name.
Here is a playground on CodeSandbox: https://codesandbox.io/s/mutable-surf-s1p4lt?file=/src/App.tsx
I intentionally delayed the transition to test === true
to better see the effect of the browser console lazy access:
useEffect(() => {
const my = document.getElementById("test");
console.log(my);
console.log(test);
// Delayed toggling of test state
setTimeout(() => setTest(true), 1000);
//setTest(true);
}, [test]);
Open both the CodeSandbox and the browser console's (they may have slightly different behaviour):
false
log of test state valueThis shows that the browser console displays objects in their state when you expand them (lazy access), which may differ from the moment you executed console.log
, and even from the preview already displayed in the console!
Note that this effect does not play in CodeSandbox console: its implementation is slightly different.
Now to come back to your original issue, let's remove the delay (setTimeout
).
We get back the original behaviour: all element logs display only the "final" class name (with opacity 1). This is very probably because by the time the console.log
accesses the element to display its information, React already performed a new rendering pass (due to the setTest(true)
in the useEffect
).
This shows BTW that this element is actually the same between re-renders: React does a good job at re-using DOM elements.
We can see this last effect by forcing React to use a different DOM element, e.g. by specifying a changing key
prop:
<ProfileHomeContainer id="test" test={test ? 1 : 0} key={test.toString()}>
https://codesandbox.io/s/interesting-parm-mwgs9t?file=/src/App.tsx
Because the value passed to the key
prop changes, React no longer re-uses the DOM element, but creates a new element, and the log is now as expected:
But this also breaks the transition
effect, which must be applied on the same element to work.