Rerender View on Browser Resize with React

Rerender view on browser resize with React

Using React Hooks:

You can define a custom Hook that listens to the window resize event, something like this:

import React, { useLayoutEffect, useState } from 'react';

function useWindowSize() {
const [size, setSize] = useState([0, 0]);
useLayoutEffect(() => {
function updateSize() {
setSize([window.innerWidth, window.innerHeight]);
}
window.addEventListener('resize', updateSize);
updateSize();
return () => window.removeEventListener('resize', updateSize);
}, []);
return size;
}

function ShowWindowDimensions(props) {
const [width, height] = useWindowSize();
return <span>Window size: {width} x {height}</span>;
}

The advantage here is the logic is encapsulated, and you can use this Hook anywhere you want to use the window size.

Using React classes:

You can listen in componentDidMount, something like this component which just displays the window dimensions (like <span>Window size: 1024 x 768</span>):

import React from 'react';

class ShowWindowDimensions extends React.Component {
state = { width: 0, height: 0 };
render() {
return <span>Window size: {this.state.width} x {this.state.height}</span>;
}
updateDimensions = () => {
this.setState({ width: window.innerWidth, height: window.innerHeight });
};
componentDidMount() {
window.addEventListener('resize', this.updateDimensions);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateDimensions);
}
}

How do I prevent component rerendering on browser resize?

I would just debounce the updateSize function so the "resize" handler calls that instead and doesn't wail on your React component state updates.

Using a debouncing utility from a tried and tested library is probably best, i.e. debounce from lodash. I'm using a 200ms delay, but obviously you should test this and tune this delay value to suit your needs.

import { debounce } from 'lodash';

function useWindowSize() {
const [size, setSize] = useState([0, 0]);

useLayoutEffect(() => {
function updateSize() {
setSize([window.innerWidth, window.innerHeight]);
}

const debouncedUpdateSize = debounce(updateSize, 200);

window.addEventListener('resize', debouncedUpdateSize);
updateSize();
return () => window.removeEventListener('resize', debouncedUpdateSize);
}, []);

return size;
}

If you don't want to add another dependency to your project you can roll your own simple debounce utility function.

const debounce = (fn, delay) => {
let timerId;
return (...args) => {
clearTimeout(timerId);
timerId = setTimeout(fn, delay, [...args]);
};
};

How to observe when window resize stop in React

There is no real "resizing state" in the browser; the resize event occurs and then it's over.

You could emulate a resizing state of your own by setting a state variable to true on every resize event, and start a new timeout that will reset it back to false after a small amount of time.

Example

import React, { useState, useEffect } from "react";

export default function App() {
const [windowResizing, setWindowResizing] = useState(false);

useEffect(() => {
let timeout;
const handleResize = () => {
clearTimeout(timeout);

setWindowResizing(true);

timeout = setTimeout(() => {
setWindowResizing(false);
}, 200);
}
window.addEventListener("resize", handleResize);

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

return <div>{JSON.stringify({ windowResizing })}</div>;
}

How to set window resize event listener value to React State?

This could probably be extracted into a custom hook. There's a few things you'd want to address:

  1. Right now you default the state to true, but when the component loads, that may not be correct. This is probably why you see an incorrect console log on the first execution of the effect. Calculating the initial state to be accurate could save you some jank/double rendering.
  2. You aren't disconnecting the resize listener when the component unmounts, which could result in an error attempting to set state on the component after it has unmounted.

Here's an example of a custom hook that addresses those:

function testIsDesktop() {
if (typeof window === 'undefined') {
return true;
}
return window.innerWidth >= 768;
}

function useIsDesktopSize() {
// Initialize the desktop size to an accurate value on initial state set
const [isDesktopSize, setIsDesktopSize] = useState(testIsDesktop);

useEffect(() => {
if (typeof window === 'undefined') {
return;
}

function autoResize() {
setIsDesktopSize(testIsDesktop());
}

window.addEventListener('resize', autoResize);

// This is likely unnecessary, as the initial state should capture
// the size, however if a resize occurs between initial state set by
// React and before the event listener is attached, this
// will just make sure it captures that.
autoResize();

// Return a function to disconnect the event listener
return () => window.removeEventListener('resize', autoResize);
}, [])

return isDesktopSize;
}

Then to use this, your other component would look like this (assuming your custom hook is just in this same file -- though it may be useful to extract it to a separate file and import it):

import React, { useState } from 'react'
import LeftSideNavbar from './LeftSideNavbar'
import TopNavbar from './TopNavbar'

export default function PostLayout({children}) {
const isDesktopSize = useIsDesktopSize();

return (
<>
<TopNavbar isDesktopSize={isDesktopSize}/>
<main>
<LeftSideNavbar/>
{children}
</main>
</>
)
}

EDIT: I modified this slightly so it should theoretically work with a server-side renderer, which will assume a desktop size.

How to listen to width of page - React

To track the window's width in react you can make a custom hook to handle window resizing such as it is done here.

But in this concrete case I think you would be better off using CSS media queries with a mobile first approach.

.navbar-desktop {
display: none;
}

@media only screen and (min-width: 768px) {
/* For desktop: */
.navbar-desktop {
display: block;
}
.navbar-mobile {
display: none;
}
}

The code above hides the .navbar-desktop by default only showing it if the width of the viewport is greater than 768. Doing the exact opposite for the .navbar-mobile.

Change value in react js on window resize

@Jousi, try this below code works fine of window resize

class App extends React.Component {
constructor(props) {
super(props);
this.state = {
windowSize: "",
thumbWidth: 75
};
}

handleResize = e => {
const windowSize = window.innerWidth;
const thumbWidth = (windowSize >= 480 && 100) || 75;
this.setState(prevState => {
return {
windowSize,
thumbWidth
};
});
};

componentDidMount() {
window.addEventListener("resize", this.handleResize);
}

componentWillUnmount() {
window.removeEventListener("resize", this.handleResize);
}

render() {
return (
<div>
<h2>window size: {this.state.windowSize}</h2>
<h3>thumbnail width: {this.state.thumbWidth}</h3>
</div>
);
}
}

ReactDOM.render(<App />, document.getElementById("root"));

react gets stuck after i resize the browser window

You forgot a dependency list for useLayoutEffect so it's adding an event listener on every render. Add an empty list for one-time execution:

useLayoutEffect(() => {
function updateTheWidthAndHeight() {
setwidthAndHeight([window.innerWidth, 600]);
}
window.addEventListener("resize", () => {
updateTheWidthAndHeight();
});
}, []);

How to call useEffect when browser is resized

This is tricky but trivial for lots of projects.
Your useEffect will be trigged one time and the function inside will be trigged everytime the user resize the browser.

Sometimes when you resize the browser you cannot access state or set the state (using useState) so i like to put the value of the width of the window outside. This way i can reuse the values anytime with almost no conflicts.

// react using hooks
import { useState, useEffect } from 'react';

// use outside the component
let isMobile = true; // variable to check width

const MyComponent = () => {

// optional: save the width of the window using state
const [width, setWidth] = useState(window.innerWidth); // check width size of the window
const handleWindowSizeChange = () => {
setWidth(window.innerWidth);
isMobile = window.innerWidth < 700 ? true : false;
};

// call your useEffect
useEffect(() => {
window.addEventListener('resize', handleWindowSizeChange);
return () => {
window.removeEventListener('resize', handleWindowSizeChange);
};
}, []);

// view of the component
return (<h1>{isMobile}</h1>)

}


Related Topics



Leave a reply



Submit