2

I was trying to implement dynamic re-rendering of my react application using this function I found here: https://stackoverflow.com/a/19014495/7838374

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

Link to my app: https://github.com/Intetra/aubreyw

I was able to get everything working perfectly when displaying my app in the browser on my desktop. I'm using expo to run the app. The problem came when I tried to run the app on my android phone. I was getting an error at launch.

COMPONENT EXCEPTION: window.addEventListener is not a function

I was able to get it working with a solution I found here: https://stackoverflow.com/a/61470685/7838374

That solution says that the event listener for window doesn't exist in react native, so we have to mock it. I don't understand what that means. I still don't know why the solution I found worked. I would like to understand. Can someone enlighten me?

  • You want to import Dimensions from react-native and use Dimensions.addEventListener to do it. But then you also need to debounce the thing. It gets kinda complicated, but you can google it. Debounce means you wait a bit after you resize the window before calculating the resizing otherwise it'll try to resize 60+ times a seconds which kills performance – wongz Dec 04 '21 at 02:43

1 Answers1

4

The browser environment is different than the React Native environment in some ways, despite the fact that both use Javascript. This means that while the language is the same, some of the underlying features may be different, meaning they could behave differently, or exist in one place and not the other.

window.addEventListener is an example of something that we can expect to exist in the browser world, but is not implemented in React Native. This gets slightly more complicated, of course, by Expo, which allows running React Native code on the web by shimming certain features, trying to bridge some of the difference between the two worlds.

Because of the dynamic nature of Javascript, even though window.addEventListener isn't provided by React Native on iOS/Android, we can just add it to our environment by defining it ourselves. That's what the solution you found (window.addEventListener = x => x) does -- it just adds a function that doesn't actually do anything (it takes x as a parameter and returns x as a result). This is sometimes referred to as a mock -- you'll often see this in the testing world.

On React Native on the device, in my testing, your solution wouldn't produce an error, but it also wouldn't actually give you the dimensions. Luckily, you can use Dimensions to get the screen size, which Expo also exposes in the web version. So, this will looks to return the correct size on both the native app and the Expo web version:


function useWindowSize() {
  const [size, setSize] = React.useState([0, 0]);
  React.useLayoutEffect(
    () => {
      console.log("Layout effect")
      function updateSize() {
        setSize([Dimensions.get('window').width, Dimensions.get('window').height])
      }
      window.addEventListener("resize", updateSize);
      updateSize();
      return () => window.removeEventListener("resize", updateSize);
    },[]);
  return size;
}

Note, you should do some testing to see what happens when rotating, resizing, etc -- I only made sure the basic functionality works.

Here's a snack with the code in context: https://snack.expo.io/Mofut1jHa

Also note that window.removeEventListener has to be mocked as well, which you can see in the Snack.

jnpdx
  • 45,847
  • 6
  • 64
  • 94