Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
577 views
in Technique[技术] by (71.8m points)

css - How to get a react component's size (height/width) before render?

I have a react component that needs to know its dimensions ahead of time, before it renders itself.

When I'd make a widget in jquery I could just $('#container').width() and get the width of the container ahead of time when I build my component.

<div id='container'></div>

these container's dimensions are defined in CSS, along with a bunch of other containers on the page. who defines the height and width and placement of the components in React? I'm used to CSS doing that and being able to access that. But in React it seems I can only access that information after the component has rendered.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The example below uses react hook useEffect.

Working example here

import React, { useRef, useLayoutEffect, useState } from "react";

const ComponentWithDimensions = props => {
  const targetRef = useRef();
  const [dimensions, setDimensions] = useState({ width:0, height: 0 });

  useLayoutEffect(() => {
    if (targetRef.current) {
      setDimensions({
        width: targetRef.current.offsetWidth,
        height: targetRef.current.offsetHeight
      });
    }
  }, []);

  return (
    <div ref={targetRef}>
      <p>{dimensions.width}</p>
      <p>{dimensions.height}</p>
    </div>
  );
};

export default ComponentWithDimensions;

Some Caveats

useEffect will not be able to detect it's own influence to width and height

For example if you change the state hook without specifying initial values (eg const [dimensions, setDimensions] = useState({});), the height would read as zero when rendered because

  • no explicit height was set on the component via css
  • only content drawn before useEffect can be used to measure width and height
  • The only component contents are p tags with the height and width variables, when empty will give the component a height of zero
  • useEffect will not fire again after setting the new state variables.

This is probably not an issue in most use cases, but I thought I would include it because it has implications for window resizing.

Window Resizing

I also think there are some unexplored implications in the original question. I ran into the issue of window resizing for dynamically drawn components such as charts.

I'm including this answer even though it wasn't specified because

  1. It's fair to assume that if the dimensions are needed by the application, they will probably be needed on window resize.
  2. Only changes to state or props will cause a redraw, so a window resize listener is also needed to monitor changes to the dimensions
  3. There's a performance hit if you redraw the component on every window resize event with more complex components. I found introducing setTimeout and clearInterval helped. My component included a chart, so my CPU spiked and the browser started to crawl. The solution below fixed this for me.

code below, working example here

import React, { useRef, useLayoutEffect, useState } from 'react';

const ComponentWithDimensions = (props) => {
    const targetRef = useRef();
    const [dimensions, setDimensions] = useState({});

    // holds the timer for setTimeout and clearInterval
    let movement_timer = null;

    // the number of ms the window size must stay the same size before the
    // dimension state variable is reset
    const RESET_TIMEOUT = 100;

    const test_dimensions = () => {
      // For some reason targetRef.current.getBoundingClientRect was not available
      // I found this worked for me, but unfortunately I can't find the
      // documentation to explain this experience
      if (targetRef.current) {
        setDimensions({
          width: targetRef.current.offsetWidth,
          height: targetRef.current.offsetHeight
        });
      }
    }

    // This sets the dimensions on the first render
    useLayoutEffect(() => {
      test_dimensions();
    }, []);

    // every time the window is resized, the timer is cleared and set again
    // the net effect is the component will only reset after the window size
    // is at rest for the duration set in RESET_TIMEOUT.  This prevents rapid
    // redrawing of the component for more complex components such as charts
    window.addEventListener('resize', ()=>{
      clearInterval(movement_timer);
      movement_timer = setTimeout(test_dimensions, RESET_TIMEOUT);
    });

    return (
      <div ref={ targetRef }>
        <p>{ dimensions.width }</p>
        <p>{ dimensions.height }</p>
      </div>
    );
}

export default ComponentWithDimensions;

re: window resizing timeout - In my case I'm drawing a dashboard with charts downstream from these values and I found 100ms on RESET_TIMEOUT seemed to strike a good balance for me between CPU usage and responsiveness. I have no objective data on what's ideal, so I made this a variable.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...