How to build a responsive chart with React and d3.js


Most of the viz components of this gallery accept a width and a height property. Yet, most of the time the dimensions of the viz we're building are unknown. We just want them to fit their container. Let's build a hookthat retrieves the parent container dimension and passes it to the viz.

This density chart is responsive! try to resize your window to see how the graph fits.

-A hook to get a div's dimension

The method I use on this website to make my chart responsive uses a hook. Hooks let you use y and other React features without writing a class as explained in the doc


This hook is basically a function that checks the offsetWidth and offsetHeight of a ref that is provided as input.


An event listener to the resize event is added to the window, to make sure the dimension is updated when the window size changes.


That's how the hook looks like:

export const useDimensions = (targetRef: React.RefObject<HTMLDivElement>) => {

  const getDimensions = () => {
    return {
      width: targetRef.current ? targetRef.current.offsetWidth : 0,
      height: targetRef.current ? targetRef.current.offsetHeight : 0
    };
  };

  const [dimensions, setDimensions] = useState(getDimensions);

  const handleResize = () => {
    setDimensions(getDimensions());
  };

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

  useLayoutEffect(() => {
    handleResize();
  }, []);

  return dimensions;
}

-Using the hook

Using the useDimensions hook described above is pretty straight-forward. You first need to create a ref using the react useRef()function:

const chartRef = useRef(null);

Then, pass this newly created ref to the hook:

const chartSize = useDimensions(chartRef);

Last but not least, do not forget to pass this ref to the container you want to track. You now have an object called chartSize here that has 2 properties, height and width. You can use those properties for your chart component.

return(
  <div ref={chartRef}>
    <MyChartComponent
      height={chartSize.height}
      width={chartSize.width}
  </div>
)

Here is an application with a ViolinPlot component that accepts a width and a height property, becoming responsive thanks to this hook:


-Caveat

Remember that the element we are tracking needs to have a height and a width. Otherwise the hook will basically return nothing.

→ Container is displayed as inline

An html elemente that is displayed as inline (display: inline;) cannot have a width and height. spanelements are inline by default.

→ By default, a div has no height

By default, the width of a div is 100%, and its height fits its content. Which means that with no content, there is no height.

→ Width 100% is ignored, flex example

By default, the width of a div is 100%, and its height fits its content. Which means that with no content, there is no height.

→ Mind the border

Josh Comeau post about border being part of the main box.




General Knowledge

Contact

👋 Hey, I'm Yan and I'm currently working on this project!

Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!

    Contact

    👋 Hey, I'm Yan and I'm currently working on this project!

    Feedback is welcome ❤️. You can fill an issue on Github, drop me a message on Twitter, or even send me an email pasting yan.holtz.data with gmail.com. You can also subscribe to the newsletter to know when I publish more content!