From 372ac57d0e1868de5ccd37b22efb0ddf73d0e534 Mon Sep 17 00:00:00 2001
From: Holtz Yan
- Most of the visualization components in this gallery accept{' '}
-
However, we often don't know the exact dimensions we need for our
@@ -143,14 +144,16 @@ export const DensityPlot = ({ width, height, data }: DensityProps) => {
- Let's assume we have a div that is responsive and takes the full width
- of an app.
+ Let's assume we have a
- How can we draw a chart inside this div that also takes up the{' '}
- entire space and remains responsive?
+ How can we draw a chart inside this
+ Let's find out! 🙇width
and height
properties.
+ To build a graph, you need to know the width
and{' '}
+ height
of the SVG area.
Let's Code
div
that is responsive and takes the
+ full width of an app.
div
that also takes up
+ the entire space and remains responsive?
+
Explain more why a hook is what we need.
++ That's exactly what we need! A function that we can reuse in all + our chart components and that manages its internal state. +
That's how the hook looks like:
@@ -100,8 +104,10 @@ export const useDimensions = (targetRef: React.RefObjectwindow
to ensure the dimensions are updated when the window
size changes.
- Why do I have the useLayoutEffect in there?
-Explain more in depth how it works.
+
+ I do not want to go too much in depth on how it works. More importantly,
+ let's see
- We have a hook that listens for changes in a div's dimensions and - returns those dimensions. -
-
- Now, it's time to use this hook to inject the dimensions into our
+ Our use-dimensions
hook monitors changes in a{' '}
+ div
's dimensions and returns those values. Now, let’s{' '}
+ use this hook to feed the dimensions directly into our
graph!
- Start by creating a ref
using the React{' '}
+ Begin by creating a ref
with the React{' '}
useRef()
function.
- A ref
allows you to target and interact with a specific
- HTML element in the DOM of your application.
+ A ref
lets you directly target and interact with a specific
+ HTML element in your app’s DOM. Remember, we covered this concept in the
+ module on D3.js axes.
@@ -59,7 +74,7 @@ export default function Home() {
Then, pass this newly created chartRef
to the hook:
height
and width
. These properties
can be used in your chart component. 🔥
- + +Pro tip: Before building a responsive visualization, use{' '}console.log()
to check thechartSize
object and ensure everything is working correctly. @@ -81,15 +97,44 @@ export default function Home() { // */}📊 Application
+You’re all set!
- You’re all set! Just pass the values from
chartSize
to the - visualization component, and it will become responsive. + Just pass the values fromchartSize
to the visualization + component, and it will become responsive.Here is a full example with code:
+ {/* - +- +- +- +- +- +- +- +- */} + +Exercices
+Responsive div ↔️, + content: , + }, + { + title: ( + + First use of the use-dimensions
hook 🫧 + + ), + content:, + }, + ]} + /> ); } @@ -111,3 +156,71 @@ return( ) `.trim(); + +const exercices: Exercise[] = [ + { + whyItMatters: ( + <> + CSS knows very well how to handle responsiveness. But do you?
++ There are numerous caveats to keep in mind! For instance, a{' '} +
+div
does not have aheight
by default. ++ I strongly encourage you to put a bg color to your chart container to + double check it behaves as expected. +
+ > + ), + toDo: ( ++
+ ), + practiceSandbox: 'exercise/ResponsiveDivSolutionPractice', + solutionSandbox: 'exercise/ResponsiveDivSolutionSolution', + }, + { + whyItMatters: ( + <> +- + Open the
+Graph
component. +- + Make it render a div with a width of 100% and a grey background. +
+- + Inside this container, render another div with a height of 300px, add + margins of 30px, and set a red background. +
+Direct application of this lesson and the previous.
+ > + ), + toDo: ( + <> ++ The red rectangle is responsive. Let's find its{' '} +
+width
andheight
! ++
+ > + ), + practiceSandbox: 'exercise/ResponsiveDivUseDimensionsPractice', + solutionSandbox: 'exercise/ResponsiveDivUseDimensionsSolution', + }, +]; diff --git a/util/lessonList.tsx b/util/lessonList.tsx index 7a354454..f6cbc1e7 100644 --- a/util/lessonList.tsx +++ b/util/lessonList.tsx @@ -345,7 +345,7 @@ export const lessonList: Lesson[] = [ ), readTime: 4, link: '/course/responsiveness/use-dimension-hook', - status: 'wip', + status: 'free', moduleId: 'responsiveness', }, { diff --git a/viz/exercise/ResponsiveDivPractice/Graph.tsx b/viz/exercise/ResponsiveDivPractice/Graph.tsx new file mode 100644 index 00000000..5be66780 --- /dev/null +++ b/viz/exercise/ResponsiveDivPractice/Graph.tsx @@ -0,0 +1,7 @@ +export const Graph = () => { + return ( +- + Create a new file named
+use-dimensions.tsx
, and copy + the content of theUseDimensions
hook into it. +- + Set up a
+chartRef
and attach it to the red rectangle{' '} +div
. +- + Use the hook to retrieve the dimensions and display them within the + red rectangle using
+p
elements. +- + Resize the window to verify that the dimensions update as expected. +
++ ++ ); +}; diff --git a/viz/exercise/ResponsiveDivPractice/index.js b/viz/exercise/ResponsiveDivPractice/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/ResponsiveDivPractice/index.js @@ -0,0 +1,6 @@ +// File used to render something in codesandbox only +import ReactDOM from 'react-dom'; +import { Graph } from './Graph'; + +const rootElement = document.getElementById('root'); +ReactDOM.render(, rootElement); diff --git a/viz/exercise/ResponsiveDivPractice/package.json b/viz/exercise/ResponsiveDivPractice/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/ResponsiveDivPractice/package.json @@ -0,0 +1,28 @@ +{ + "name": "pie-chart-basic", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "index.js", + "dependencies": { + "react": "17.0.2", + "react-dom": "17.0.2", + "react-scripts": "4.0.0" + }, + "devDependencies": { + "@babel/runtime": "7.13.8", + "typescript": "4.1.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/viz/exercise/ResponsiveDivSolution/Graph.tsx b/viz/exercise/ResponsiveDivSolution/Graph.tsx new file mode 100644 index 00000000..c6442928 --- /dev/null +++ b/viz/exercise/ResponsiveDivSolution/Graph.tsx @@ -0,0 +1,12 @@ +export const Graph = () => { + return ( + + ++ ); +}; diff --git a/viz/exercise/ResponsiveDivSolution/index.js b/viz/exercise/ResponsiveDivSolution/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/ResponsiveDivSolution/index.js @@ -0,0 +1,6 @@ +// File used to render something in codesandbox only +import ReactDOM from 'react-dom'; +import { Graph } from './Graph'; + +const rootElement = document.getElementById('root'); +ReactDOM.render(, rootElement); diff --git a/viz/exercise/ResponsiveDivSolution/package.json b/viz/exercise/ResponsiveDivSolution/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/ResponsiveDivSolution/package.json @@ -0,0 +1,28 @@ +{ + "name": "pie-chart-basic", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "index.js", + "dependencies": { + "react": "17.0.2", + "react-dom": "17.0.2", + "react-scripts": "4.0.0" + }, + "devDependencies": { + "@babel/runtime": "7.13.8", + "typescript": "4.1.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/viz/exercise/ResponsiveDivUseDimensionsPractice/Graph.tsx b/viz/exercise/ResponsiveDivUseDimensionsPractice/Graph.tsx new file mode 100644 index 00000000..c6442928 --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsPractice/Graph.tsx @@ -0,0 +1,12 @@ +export const Graph = () => { + return ( + + ++ ); +}; diff --git a/viz/exercise/ResponsiveDivUseDimensionsPractice/index.js b/viz/exercise/ResponsiveDivUseDimensionsPractice/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsPractice/index.js @@ -0,0 +1,6 @@ +// File used to render something in codesandbox only +import ReactDOM from 'react-dom'; +import { Graph } from './Graph'; + +const rootElement = document.getElementById('root'); +ReactDOM.render(, rootElement); diff --git a/viz/exercise/ResponsiveDivUseDimensionsPractice/package.json b/viz/exercise/ResponsiveDivUseDimensionsPractice/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsPractice/package.json @@ -0,0 +1,28 @@ +{ + "name": "pie-chart-basic", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "index.js", + "dependencies": { + "react": "17.0.2", + "react-dom": "17.0.2", + "react-scripts": "4.0.0" + }, + "devDependencies": { + "@babel/runtime": "7.13.8", + "typescript": "4.1.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/viz/exercise/ResponsiveDivUseDimensionsSolution/Graph.tsx b/viz/exercise/ResponsiveDivUseDimensionsSolution/Graph.tsx new file mode 100644 index 00000000..7f2b38c2 --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsSolution/Graph.tsx @@ -0,0 +1,24 @@ +import { useRef } from 'react'; +import { useDimensions } from './use-dimensions'; + +export const Graph = () => { + const chartRef = useRef(null); + const chartSize = useDimensions(chartRef); + + return ( + ++ ); +}; diff --git a/viz/exercise/ResponsiveDivUseDimensionsSolution/index.js b/viz/exercise/ResponsiveDivUseDimensionsSolution/index.js new file mode 100644 index 00000000..fa564d27 --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsSolution/index.js @@ -0,0 +1,6 @@ +// File used to render something in codesandbox only +import ReactDOM from 'react-dom'; +import { Graph } from './Graph'; + +const rootElement = document.getElementById('root'); +ReactDOM.render(++Width: {chartSize.width}
+Height: {chartSize.height}
+, rootElement); diff --git a/viz/exercise/ResponsiveDivUseDimensionsSolution/package.json b/viz/exercise/ResponsiveDivUseDimensionsSolution/package.json new file mode 100644 index 00000000..ac08117a --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsSolution/package.json @@ -0,0 +1,28 @@ +{ + "name": "pie-chart-basic", + "version": "1.0.0", + "description": "", + "keywords": [], + "main": "index.js", + "dependencies": { + "react": "17.0.2", + "react-dom": "17.0.2", + "react-scripts": "4.0.0" + }, + "devDependencies": { + "@babel/runtime": "7.13.8", + "typescript": "4.1.3" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/viz/exercise/ResponsiveDivUseDimensionsSolution/use-dimensions.tsx b/viz/exercise/ResponsiveDivUseDimensionsSolution/use-dimensions.tsx new file mode 100644 index 00000000..b83dd0dc --- /dev/null +++ b/viz/exercise/ResponsiveDivUseDimensionsSolution/use-dimensions.tsx @@ -0,0 +1,29 @@ +import { useEffect, useLayoutEffect, useState } from 'react'; + +// Hook to retrieve the dimensions of a div. +// react-graph-gallery.com +export const useDimensions = (targetRef: React.RefObject ) => { + 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; +};