For the majority of the time in our React components, the layout can be handled by modules such as Flexbox, CSS Grid, or with some custom CSS. Occasionally, however, we need to render differently depending on the size of the browser window and, therefore, handle the browser resize
events to change what is rendered.
This guide will show you how to use React to render components when the window is resized. First we will do so by simply displaying the width and height of the window. Then we’ll use a more 'real world' scenario by scaling drawing on a canvas depending on its size.
To show the width and height of a window and have the component render when this is changed, we first need to store these values in the component state.-That way, when they are changed, this will trigger a render. They can be initialized using the current innerWidth
and innerHeight
like this:
1const [width, setWidth] = React.useState(window.innerWidth);
2const [height, setHeight] = React.useState(window.innerHeight);
This code uses the state hook; this hook returns an array and the code uses array destructuring to get values for width
and height
and functions that can be used to update the values.
The values are displayed in the component like this:
1return (
2 <div>
3 <div>{`Window width = ${width}`}</div>
4 <div>{`Window height = ${height}`}</div>
5 </div>);
All that is left to do is update the state when the window is resized. We need a function to do the update:
1const updateWidthAndHeight = () => {
2 setWidth(window.innerWidth);
3 setHeight(window.innerHeight);
4};
To listen for the resize event, we need to add an event listener, which is done in the Effect hook. In the following code useEffect
is called with no second parameter, so it will run every time the component is rendered. It adds an event listener for the resize
event that calls the function to update the component state. Event listeners must also be removed when they are no longer needed and this is done using the return value of the effect hook. This will be executed both when the component is unmounted and, also, before executing the effect again which calls window.removeEventListener
to clean up:
1React.useEffect(() => {
2 window.addEventListener("resize", updateWidthAndHeight);
3 return () => window.removeEventListener("resize", updateWidthAndHeight);
4});
Now, when this component is on a page, it will update the display of the width and height whenever the browser window is resized.
The full code for this component can be found here.
A more 'real world' scenario for handling resize
events is when drawing on a canvas. It is very likely that the scale of the canvas will need to change as the size of the display changes. We will now build a component that will render a canvas, draw some shapes on it, and redraw those shapes at the relevant scale whenever the browser is resized.
First, we need a function that will take a canvas, scale as a parameter, and draw something on it:
1function draw(canvas, scaleX, scaleY) {
2 const context = canvas.getContext("2d");
3 context.scale(scaleX, scaleY);
4 context.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
5
6 context.beginPath();
7 context.setLineDash([]);
8 context.lineWidth = 2;
9 context.strokeStyle = "red";
10 context.moveTo(0, 100);
11 context.lineTo(scaleWidth, 100);
12 context.moveTo(0, 400);
13 context.lineTo(scaleWidth, 400);
14 context.stroke();
15 context.lineWidth = 1;
16 context.strokeStyle = "blue";
17 context.fillStyle = "blue";
18 context.rect(200, 200, 100, 100);
19 context.fill();
20 context.closePath();
21}
This function gets a 2D drawing context, scales it according to the parameters, clears it, and draws two lines and a solid rectangle.
We also need a canvas
element to be drawn on. We need to keep a reference to this canvas using the useRef hook; this allows the canvas element to be accessed elsewhere within the component code:
1const canvas = React.useRef(null);
2return <canvas ref={canvas} style={{ width: "100%", height: "100%" }} />;
The ref is initialized to null
and is set to the canvas element by the ref
attribute on the element. To access the referenced canvas element canvas.current
is used.
We will need a function to be called when the canvas is resized that will set the canvas width
and height
properties to the actual width and height of the canvas and will set the scale from the current window size. For the purposes of this guide, this function that uses a width and height of 500 to be a scale of one will be used:
1const calculateScaleX = () => canvas.current.clientWidth / scaleWidth;
2const calculateScaleY = () => canvas.current.clientHeight / scaleHeight;
3
4const resized = () => {
5 canvas.current.width = canvas.current.clientWidth;
6 canvas.current.height = canvas.current.clientHeight;
7 setScale({ x: calculateScaleX(), y: calculateScaleY() });
8};
The scale needs to be stored in component state and will be initialized to one on both axes like this:
1const [scale, setScale] = React.useState({ x: 1, y: 1 });
Whenever the browser window is resized, we need to calculate the scale and update the value stored in the component state. As in the previous example, this is done using the Effect hook with no second parameter to add and remove listeners for the resize
event, this time calling the resized
function:
1React.useEffect(() => {
2 const currentCanvas = canvas.current;
3 currentCanvas.addEventListener("resize", resized);
4 return () => currentCanvas.removeEventListener("resize", resized);
5});
Finally, we need to draw on the canvas whenever the scale changes. To do this we can, again, use the Effect hook but, this time, we will give it a second parameter of the scale state value. This means that the hook will only execute when the scale is changed. Inside the hook, we simply need to call the draw
function passing the canvas and scales as parameters:
1React.useEffect(() => {
2 draw(canvas.current, scale.x, scale.y);
3}, [scale]);
The component now changes the scale of the canvas and redraws it whenever the browser is resized. The code for this component can be found here.
This guide has shown how to listen for resize
events and render components when the window is resized. The patterns used to listen for the events and clean up can also be used for other window events.
The code for the two example components can be found here - Display Window Dimensions and Scaling a Canvas.