Search code examples
reactjstypescriptreact-hooksreact-grid-layout

React get size of parent element within ResponsiveReactGridLayout


I'm trying to render a dashboard using ResponsiveReactGridLayout and my code in the functional component is as follows:


const Home = () => {
  const [coins, setCoins] = useState(latestCoins);
  const [coin, setCoin] = useState(curCoin);
  const canvasRef = useRef(null);

  useEffect(() => {
    getCoins().then((data) => setCoins(data));
  }, []);
  return (
    <ResponsiveReactGridLayout
      onResize={(e) => console.log(e)}
      className="layout"
      layouts={layouts}
      rowHeight={100}
      breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
      cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
    >
     
      <div key="candlestick" className="home-card" ref={canvasRef}>
        {console.log(canvasRef)}
        //Trying to use canvasRef.current.width and height but getting an error: Property 'width' does not exist on type 'never'
        <Candlestick coin={coin} width={600} height={400} />
      </div>
    </ResponsiveReactGridLayout>
  );
};

My Candlestick component does return a ChartCanvas object from react-stockcharts, which needs a width and height (without which it does not take up the entire space of the div)

How can I get the height and width from the div?

I've tried using the useRef() hook, but it always seems to have current as null.

Could I get some help?


Solution

  • Property 'width' does not exist on type 'never'

    This is actually a TypeScript error. It means that TypeScript doesn't know what the type of the .current property is supposed to be. Therefore it doesn't know that .current has properties .width and .height and it prevents you from accessing them. You need to tell TypeScript that this is a ref to a div.

    An HTMLDivElement doesn't actually have .width and .height, but you can use .clientWidth or .offsetWidth instead.

    const canvasRef = useRef<HTMLDivElement>(null);
    

    I've tried using the useRef() hook, but it always seems to have current as null.

    The ResponsiveReactGridLayout component is setting its own refs on its children, so your ref={canvasRef} gets overwritten.

    The simplest way around this is to add another layer of nesting. The ResponsiveReactGridLayout will set a ref on the outermost div, but you can add another div inside of it with a ref that you control. Make sure that it fills up the entire height.

    Note that the .current property might still be null on the first render. You can use a default value, as suggested by @PushpikaWan, or you can delay rendering the chart until you have an actual width.

    <ResponsiveReactGridLayout /*...props...*/ >
        <div key="candlestick" className="box">
            <div ref={canvasRef} style={{ width: "100%", height: "100%" }}>
                {canvasRef.current ? (
                    <Candlestick
                        data={data}
                        width={canvasRef.current.clientWidth}
                        height={canvasRef.current.offsetHeight}
                    />
                ) : null}
            </div>
        </div>
        /*...other children...*/
    </ResponsiveReactGridLayout>
    

    I needed to add an explicit width on the grid layout in order to get this to work. I'm not sure if you are doing this part already, but you can use the WidthProvider as explained in the docs.

    import { Responsive, WidthProvider } from 'react-grid-layout';
    
    const ResponsiveReactGridLayout = WidthProvider(Responsive);
    

    CodeSandbox Demo