8

I want to detect screen orientation changes using the event orientationchange, which is supported by all major mobile browsers.

I add the event listener in componentDidMount and set the state from inside the event callback.

However, I see that when the event first fires as a result of change from portrait to landscape the state is not updated to landscape. Then when I change the orientation from landscape back to portrait, the state says the orientation is landscape. After this each time I change the orientation the state always says the opposite of the actual orientation. I'm not sure if I should be using some lifecycle react method or my detection is not good.

I tested the code using Chrome developer tools Toggle device toolbar. This is my code:

import React from 'react';

class AppInfo extends React.Component {
  state = {
    screenOrientation: 'portrait'
  }

  isPortraitMode = () => {
    console.log(this.state);
    const { screenOrientation } = this.state;
    return screenOrientation === 'portrait';
  }

  setScreenOrientation = () => {
    if (window.matchMedia("(orientation: portrait)").matches) {
      console.log('orientation: portrait');
      this.setState({
        screenOrientation: 'portrait'
      });
    }

    if (window.matchMedia("(orientation: landscape)").matches) {
      console.log('orientation: landscape');
      this.setState({
        screenOrientation: 'landscape'
      });
    }
  }

  componentDidMount() {
    window.addEventListener('orientationchange', this.setScreenOrientation);
  }

  render() {
    console.log(`orientation: from render: isPortraitMode = ${this.isPortraitMode()}`);
    <div>
      Hello
    </div>
  }
}

export default AppInfo;
hitchhiker
  • 1,099
  • 5
  • 19
  • 44

4 Answers4

2

You could try triggering it with the "resize" event instead, as some devices don't provided the orientationchange event, but do fire the window's resize event.

window.addEventListener("resize", this.setScreenOrientation);
Carlene
  • 357
  • 2
  • 12
2

orientationchange has been deprecated and it might or might not work in some browsers. Best bet now is to use one of the following options:

window.screen.orientation.addEventListener('change', function(e) { ... })
window.screen.orientation.onchange = function(e) { ... }

This way you can put whatever logic you want in the callback part of the listener, react or not.

Pepe Alvarez
  • 1,218
  • 1
  • 9
  • 15
2

Here is an implementation using hooks for react that tracks screen orientation using a custom hook from GitHub for obtaining and tracking orientation. It creates the event listener every time the component is mounted using useEffect and it shuts off the listener everytime the component is dismounted:

import {useState, useEffect} from 'react'

const getOrientation = () =>
  window.screen.orientation.type

const useScreenOrientation = () => {
  const [orientation, setOrientation] =
    useState(getOrientation())

  const updateOrientation = event => {
    setOrientation(getOrientation())
  }

  useEffect(() => {
    window.addEventListener(
      'orientationchange',
      updateOrientation
    )
    return () => {
      window.removeEventListener(
        'orientationchange',
        updateOrientation
      )
    }
  }, [])

  return orientation
}

export default useScreenOrientation

In my Android mobile the two values for orientation are: 'portrait-primary' and 'landscape-secondary'. In my Ubuntu system it is: 'landscape-primary'. In my Windows 10 Surface Book 2 laptop mode: 'protrait-primary'. Doesn't handle the switch to tablet well. And it doesn't detect the orientation change at all. All this is under Chrome v 99.0.4844.84 (official build) (64 bit). The Mac OS Monterey v 12.2.1 reports 'landscape-primary' same version of Chrome.

Julio Spinelli
  • 587
  • 3
  • 16
0

Your program works just fine, you just need to log it like this:

this.setState({screenOrientation: 'landscape'}, console.log('orientation: landscape'))

The reason behind it is that call to setState isn't synchronous but setState(updater, callback) is an async function and it takes the callback as second argument.

Vahid Al
  • 1,581
  • 1
  • 12
  • 24