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

Time:09-27

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

CodePudding user response:

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
        }
    })
}

CodePudding user response:

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} />
  • Related