Lars van der Niet
Lars van der Niet

Reputation: 31

Why are my dimensions not updating on resize when using React Hooks

My dimensions are not updating whenever the window is resized. In the code below you can see the window.innerHeight is updated, but the dimensions are not. I am probably missing something but I have not figured it out yet.

// Navbar.components.ts:

export const sidebar = () => {
    let height;
    let width;

    if (typeof window !== `undefined`) {
        height = window.innerHeight
        width = window.innerWidth
    }

    const [dimensions, setDimensions] = useState({
        windowHeight: height,
        windowWidth: width,
    })

    useEffect(() => {
        const debouncedHandleResize = debounce(function handleResize() {
            setDimensions({
                windowHeight: window.innerHeight,
                windowWidth: window.innerWidth,
            });
            // Logging window.innerHeight gives the current height,
            // Logging dimensions.windowHeight gives the initial height
            console.log(window.innerHeight, " . ", dimensions.windowHeight)
        }, 100);

        window.addEventListener(`resize`, debouncedHandleResize)
        return () => window.removeEventListener('resize', debouncedHandleResize)

    }, [])

    return {
        open: () => ({
            clipPath: `circle(${dimensions.windowHeight * 2 + 200}px at 40px 40px)`,
            transition: {
                type: "spring",
                stiffness: 20,
                restDelta: 2
            }
        }),
        closed: () => ({
            clipPath: `circle(30px at ${300 - 40}px ${dimensions.windowHeight - 45}px)`,
            transition: {
                delay: 0.2,
                type: "spring",
                stiffness: 400,
                damping: 40
            }
        })
    }
}

And I use the sidebar like this:

// Navbar.tsx

const Navbar: React.FC<NavbarProps> = () => {
  ...
  
  return {
      ...
      <MobileNavBackground variants={sidebar()} />
      ...
  }
}

Here is an example of the logs that are returned when resizing the window:

Logs that are returned

Update 1

@sygmus1897

Code changed to this:

// Navbar.tsx: 

const Navbar: React.FC<NavbarProps> = () => {

  const [windowWidth, windowHeight] = getDimensions();
  useEffect(() => {
  }, [windowWidth, windowHeight])

  return (
     ...
     <MobileNavWrapper
        initial={false}
        animate={menuIsOpen ? "open" : "closed"}
        custom={height}
        ref={ref}
        menuIsOpen={menuIsOpen}
      >
        <MobileNavBackground variants={sidebar} custom={windowHeight} />
        <MobileNav menuIsOpen={menuIsOpen} toggleMenu={toggleMenu} />
        <MenuToggle toggle={() => toggleMenu()} />
      </MobileNavWrapper>
   )
}
// getDimensions()
export const getDimensions = () => {
    const [dimension, setDimension] = useState([window.innerWidth, window.innerHeight]);

    useEffect(() => {
        window.addEventListener("resize", () => {
            setDimension([window.innerWidth, window.innerHeight])
        });
        return () => {
            window.removeEventListener("resize", () => {
                setDimension([window.innerWidth, window.innerHeight])
            })
        }
    }, []);
    
    return dimension;

};
// Navbar.components.ts

export const sidebar = {
    open: (height) => ({
        clipPath: `circle(${height + 200}px at 40px 40px)`,
        transition: {
            type: "spring",
            stiffness: 20,
            restDelta: 2
        }
    }),
    closed: (height) => ({
        clipPath: `circle(30px at ${300 - 60}px ${height - 65}px)`,
        transition: {
            delay: 0.2,
            type: "spring",
            stiffness: 400,
            damping: 40
        }
    })
}

The issue remains where resizing the window does not affect the clipPath position of the circle. To illustrate the issue visually, the hamburger is supposed to be inside the green circle: Issue

Upvotes: 1

Views: 2578

Answers (2)

Lars van der Niet
Lars van der Niet

Reputation: 31

Thanks to this thread: Framer Motion - stale custom value - changing the custom value doesn't trigger an update I found that the issue I'm having is a bug in framer-motion. To resolve this issue, add a key value to the motion component that's having issues re-rendering. This makes sure React re-renders the component.

In my case all I had to do was this:

<MobileNavBackground variants={sidebar} custom={windowHeight} key={key} />

Upvotes: 0

sygmus1897
sygmus1897

Reputation: 41

You can make a custom hook to listen to window resize.

You can modify solution from this link as per you requirement Custom hook for window resize

By using useState instead of ref, updating it on resize and returning the values to your main component

Here's an example:

export default function useWindowResize() {
    const [dimension, setDimension] = useState([0, 0]);

    useEffect(() => {
        window.addEventListener("resize", () => {
            setDimension([window.innerWidth, window.innerHeight])
        });
        return () => {
            window.removeEventListener("resize", () => {
                setDimension([window.innerWidth, window.innerHeight])
            })
        }
    }, []);
    
    return dimension;
}

and inside your main component use it like this:

const MainComponent = () => {
    const [width, height] = useWindowResize();
    useEffect(()=>{
        // your operations
    }, [width, height])
}

Your component will update every time the dimensions are changed. And you will get the updated width and height

EDIT:

Framer-motion provides a way to dynamically set variant's properties(for detailed guide refer to this Dynamically Update Variant) :-

// Navbar.tsx: 

const Navbar: React.FC<NavbarProps> = () => {
  return (
     ...
     <MobileNavWrapper
        initial={false}
        custom={window.innerWidth} // custom={window.innerHeight} if variable depends on Height
        animate={menuIsOpen ? "open" : "closed"}
        custom={height}
        ref={ref}
        menuIsOpen={menuIsOpen}
      >
        <MobileNavBackground variants={sidebar} />
        <MobileNav menuIsOpen={menuIsOpen} toggleMenu={toggleMenu} />
        <MenuToggle toggle={() => toggleMenu()} />
      </MobileNavWrapper>
   )
}
// Navbar.components.ts

export const sidebar = {
    open: (width) => ({ 
        clipPath: `circle(${width+ 200}px at 40px 40px)`,
        transition: {
            type: "spring",
            stiffness: 20,
            restDelta: 2
        }
    }),
    closed: (width) => ({
        clipPath: `circle(30px at ${300 - 60}px ${width- 65}px)`,
        transition: {
            delay: 0.2,
            type: "spring",
            stiffness: 400,
            damping: 40
        }
    })
}

Upvotes: 3

Related Questions