window.visualViewport.height doenst update useEffect when a dependency

Viewed 104

This ugly code works. Every second viewportHeight is set to the value of window.visualViewport.height

  const [viewportHeight, setViewportHeight] = React.useState(0);

  React.useEffect(() => {
    setInterval(() => {
      setViewportHeight(window.visualViewport.height);
    }, 1000);
  }, []);

However this doesn't work. viewportHeight is set on page load but not when the height changes.

  React.useEffect(() => {
    setViewportHeight(window.visualViewport.height);
  }, [window.visualViewport.height]);

Additional context: I need the page's height in state and I need the virtual keyboard's height to be subtracted from this on Mobile iOS.

2 Answers

You can only use state variables managed by React as dependencies - so a change in window.visualViewport.height will not trigger your effect.

You can instead create a div that spans the whole screen space and use a resize observer to trigger effects when its size changes:

import React from "react";
import useResizeObserver from "use-resize-observer";

const App = () => {

  const { ref, width = 0, height = 0 } = useResizeObserver();
  const [viewportHeight, setViewportHeight] = React.useState(height);

  React.useEffect(() => {
    setViewportHeight(window.visualViewport.height);
  }, [height]);

  return (
    <div ref={ref} style={{ width: "100vw", height: "100vh" }}>
      // ...
    </div>
  );
};

This custom hook works:

function useVisualViewportHeight() {
  const [viewportHeight, setViewportHeight] = useState(undefined);
  useEffect(() => {
    function handleResize() {
      setViewportHeight(window.visualViewport.height);
    }
    window.visualViewport.addEventListener('resize', handleResize);
    handleResize();
    return () => window.visualViewport.removeEventListener('resize', handleResize);
  }, []);
  return viewportHeight;
}
Related