1

I'm trying to understand which lifecycle point CSS styles are starting to apply to DOM element(s).

Run onto a problem where I need to get the element's clientWidth after all styles are applied. My tests shows that I can not rely on useEffect hook, because I get inconsistent result.

I test it with a simple <did/> element, which is should take full parent width (grid container).

const divRef = useRef()

useEffect(() => {
    console.log(divRef.current.clientWidth)    
})

<div class="parent">
    <div ref={divRef} class="child" />
</div>

I expect the width === 280px, but get (lets say) 980px most of the time. If I put console.log inside a timeout everything starts to work as expected. Clearly useEffect runs before styles are applied.

Can someone clarify for me how I can get reliable result?

Valeed Anjum
  • 447
  • 1
  • 5
  • 17
Andrew
  • 695
  • 1
  • 8
  • 21
  • 1
    What exactly is your effect supposed to be waiting for? E.g. A 100% width element changing size in the DOM due to a page resize is entirely outside of React's control, but you could listen for that page resize. – DBS May 18 '20 at 14:55

2 Answers2

0

You can use the second argument of useEffect to keep track of the value of a variable.

const divRef = useRef()
const [divWidth, setDivWidth] = React.useState('');

useEffect(() => {
    setDivWidth(divRef.current.clientWidth)    
}, [divWidth]) // Only re-run the effect if divWidth changes

I've also found another StackOverflow question that might help you:

How to get the width of a react element

Consuelo Sanna
  • 186
  • 1
  • 2
  • 6
0

React doesn't really get any more involved with CSS than emitting class and style attributes to DOM attributes. It never "applies" CSS, and really what you probably mean could be a conglomeration of a bunch of things that could be happening in the DOM:

  • New styles taking effect from a stylesheet having loaded
  • Layout changing due to window resizing
  • Reflow happens due to fonts being applied

See What forces layout/reflow to get an idea of the scope of this.

Sadly, HTML doesn't have a DOM event for when an element's layout changes or when reflows occur, so if you need to get element dimensions, you have to hack it. One unsophisticated technique is a polling loop that periodically checks the dimensions of the DOM element. If you don't want to continuously poll, you could maybe cut off the poller after some time and add a window resize event listener to adjust the dimensions

function YourComponent() {
  const [width, setWidth] = useState(0);
  const divRef = useRef(null);

  function checkWidth() {
    if (divRef.current && divRef.current.clientWidth !== width) {
      setWidth(divRef.current.clientWidth);
    }
  }

  useEffect(() => {
    const interval = setInterval(checkWidth, 1000);
    return () => clearInterval(interval);
  }, []);

  useEffect(() => {
    window.addEventListener('resize', checkWidth);
    return () => window.removeEventListener('resize', checkWidth);
  }, []);

  return (
    <div ref={divRef}>
      ...
    </div>
  );
}

Jacob
  • 77,566
  • 24
  • 149
  • 228