admin管理员组

文章数量:1202334

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;

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;
Share Improve this question asked Apr 13, 2019 at 8:25 hitchhikerhitchhiker 1,3196 gold badges25 silver badges57 bronze badges 2
  • 1 You might wanna see this answer - stackoverflow.com/a/36087703/1263904 – pritam Commented Apr 13, 2019 at 14:14
  • stackoverflow.com/a/64559463/9444013 – shammi Commented Oct 27, 2020 at 17:08
Add a comment  | 

5 Answers 5

Reset to default 7

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.

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.

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

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.

Here's a shorter and newer version hook, modified from the Answer:

// use-screen-orientation.js
import {useState, useEffect} from 'react';

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

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

    useEffect(() => {
        const handleOrientationChange = () => setOrientation(getOrientation());

        screen.orientation.addEventListener('change', handleOrientationChange);

        return () => screen.orientation.removeEventListener('change', handleOrientationChange);
    }, []);

    return orientation;
};

Usage

import React from "react";
import {useScreenOrientation} from "@/hooks/use-screen-orientation";

const Demo= () => {
  const screenOrientation = useScreenOrientation();

  return (
    <p>Screen orientation is: {screenOrientation}</p>
  )
}

It will return string type from: one of portrait-primary, portrait-secondary, landscape-primary, or landscape-secondary

See also in here

本文标签: