Render on Window Resize in React
Aug 2, 2019 • 8 Minute Read
Introduction
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.
Display Window Dimensions
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:
const [width, setWidth] = React.useState(window.innerWidth);
const [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:
return (
<div>
<div>{`Window width = ${width}`}</div>
<div>{`Window height = ${height}`}</div>
</div>);
All that is left to do is update the state when the window is resized. We need a function to do the update:
const updateWidthAndHeight = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
};
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:
React.useEffect(() => {
window.addEventListener("resize", updateWidthAndHeight);
return () => window.removeEventListener("resize", updateWidthAndHeight);
});
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.
Scaling a Canvas
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:
function draw(canvas, scaleX, scaleY) {
const context = canvas.getContext("2d");
context.scale(scaleX, scaleY);
context.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
context.beginPath();
context.setLineDash([]);
context.lineWidth = 2;
context.strokeStyle = "red";
context.moveTo(0, 100);
context.lineTo(scaleWidth, 100);
context.moveTo(0, 400);
context.lineTo(scaleWidth, 400);
context.stroke();
context.lineWidth = 1;
context.strokeStyle = "blue";
context.fillStyle = "blue";
context.rect(200, 200, 100, 100);
context.fill();
context.closePath();
}
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:
const canvas = React.useRef(null);
return <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:
const calculateScaleX = () => canvas.current.clientWidth / scaleWidth;
const calculateScaleY = () => canvas.current.clientHeight / scaleHeight;
const resized = () => {
canvas.current.width = canvas.current.clientWidth;
canvas.current.height = canvas.current.clientHeight;
setScale({ x: calculateScaleX(), y: calculateScaleY() });
};
The scale needs to be stored in component state and will be initialized to one on both axes like this:
const [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:
React.useEffect(() => {
const currentCanvas = canvas.current;
currentCanvas.addEventListener("resize", resized);
return () => currentCanvas.removeEventListener("resize", resized);
});
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:
React.useEffect(() => {
draw(canvas.current, scale.x, scale.y);
}, [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.
Conclusion
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.