1

I want to observer when window stop resizing, cause currently if i listen to the resize event on window, i could only listen to resizing change but not knowing when resizing stops. I checked the event parameter in resizing callback, but did not find and helpful information.

Below is the code i'm trying to complete:

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

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

  const handleWindowResize = e => {
    setWindowResizing(true);
  };

  useEffect(() => {
    window.addEventListener("resize", handleWindowResize);

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

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

But this does not work, since windowResizing will keep being true after resizing begins, even i already stops resizing.

So is there any way to observe when window stop resizing and i can then call setState based on that?

2 Answers 2

3

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>;
}
Sign up to request clarification or add additional context in comments.

4 Comments

Cheers, this is exactly what i want, however i got one small question: what's the difference between making the timeout as a ref and just putting it as a local variable in useEffect?
@Limboer Great! It's just a matter of preference really. Since the timeout variable was only needed in the effect I put it there as a regular scoped variable. If you would have liked to be able to clear the timeout in e.g. an event handler as well, a ref would have been more appropriate.
Thanks for your detailed explanation. Last question: I see you put handleResize() inside useEffect, is there any particular reason to do that? Or is it just a matter of coding preference as well? I usually prefer to put function or method independently along inside component so i can reference it in other places.
@Limboer That's mainly preference as well. Since we pass an empty array as second argument to the effect, it will only be run once after the initial render. By putting handleResize in the effect it's clear the same function will be used for addEventListener and removeEventListener. The same function would be used even if we put it outside of the effect, but it's not as obvious. This is discussed briefly in the FAQs in the documentation, and that's why I put functions in the effect.
0

Small update to Tholle's answer

to include the size and the resizing state

import { useState, useLayoutEffect } from 'react'

// https://stackoverflow.com/a/63010184
function debounce(fn, ms) {
    let timer;
    return _ => {
        clearTimeout(timer);
        timer = setTimeout(_ => {
            timer = null;
            fn.apply(this, arguments);
        }, ms);
    };
}

const useWindowSize = () => {
    const [size, setSize] = useState([0, 0, false]);
    useLayoutEffect(() => {
        let timeout;
        clearTimeout(timeout);
        function updateSize() {
            clearTimeout(timeout);
            setSize([window.innerWidth, window.innerHeight, true]);
            timeout = setTimeout(() => {
                setSize([window.innerWidth, window.innerHeight, false]);
            }, 800);
        }
        const debouncedResizeHandler = debounce(() => updateSize())
        window.addEventListener('resize', debouncedResizeHandler);
        setSize([window.innerWidth, window.innerHeight, false]);
        return () => window.removeEventListener('resize', debouncedResizeHandler);
    }, []);
    return size;
}

export default useWindowSize

1 Comment

I would strongly suggest not making size actual state. If it is, the component will update whenever it changes. The client code may not need or want that. What if it only cares when certain breakpoints are crossed, like > 500px or > 1000px? Let the calling code turn the return values into state if they'd like do. use a ref instead!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.