useScreenOrientation - React Native Snippet

Problém 🤔

Každý den se musí technik React Native vypořádat se změnami orientace obrazovky a jejich zvláštnostmi a bolestivými body, ať už jde o pouhou změnu uživatelského rozhraní na základě zářezů na šířku, spouštění nových síťových hovorů nebo případné zobrazení/upuštění klávesnice při každém otočení. přístroj. Potřebujeme tedy řešení, které nám řekne, kdykoli dojde ke změně orientace obrazovky, a odtud můžeme zjistit, jaký je nejlepší postup.

Naivní přístup 👀

OK. Nyní tedy víme, co se snažíme vyřešit. Rychlým a snadným přístupem může být rychlé porovnání šířky obrazovky s výškou obrazovky.

Něco takového:

import { Dimensions } from 'react-native'

const isLandscape = () => Dimensions.get('window').width > Dimensions.get('window').height

Nevýhody:

  1. Neřekne nám ve skutečnosti, jaká je přesná orientace obrazovky, jen zda jsme na výšku nebo na šířku.

  2. Toto řešení nám dynamicky neříká, že došlo ke změně orientace obrazovky. Jaká je orientace obrazovky, když jsme tuto funkci volali. Pokud například používám navigaci React a vložím do zásobníku novou obrazovku, mohu zjistit orientaci obrazovky v okamžiku, kdy jsem obrazovku zatlačil. Ale řekněme, že poté otočím zařízení, stále uvidím předchozí hodnotu isLandscape pokud to znovu ručně nezavolám.

Lepší řešení 💪

Chceme nastavit háček, který naslouchá změnám orientace obrazovky a způsobí vykreslení pokaždé, když se orientace obrazovky v našem stavu změní.

OK. Abychom si usnadnili život, použiji dvě knihovny, o kterých si myslím, že se zde opravdu hodí a umožní nám neponořit se do nativního kódu a sami jej překlenout:

  • react-native-orientation-locker
  • react-singleton-hook

Upozornění 🚨
React-native-orientation-locker se zdá mít na Androidu chybu, která brání posluchači v konzistentním vysílání událostí (testováno na React Native 0.65.1). Zatím tedy používám základní řešení, dokud to nebude opraveno. Bohužel ztrácíme možnost přesně vědět, v jaké orientaci obrazovky se nacházíme.

Krok 1
Nastavíme základní hák s reakčním jednoduchým háčkem, který můžeme v dalším kroku rozšířit. Důvod, proč chceme vždy jen jeden háček, je ten, že nemáme více posluchačů, kteří poslouchají změny. Pokud například znovu používáte React Navigation a vložíte do zásobníku tři obrazovky, každá z těchto obrazovek by mohla mít nastaveny posluchače a nastavovat stav, i když nejsou viditelné.

import { singletonHook } from 'react-singleton-hook'

export const useScreenOrientation = singletonHook(
  {
    isLandscape: false,
    screenOrientation: undefined,
  },
  () => {

    return {
      isLandscape: false,
      screenOrientation
    }
  },
)

Krok 2
Po nastavení základního háku nyní můžeme začít přidávat některé funkce. Nejprve nastavíme orientaci obrazovky jako počáteční orientaci obrazovky zachycenou funkcí Reagovat-nativní orientace-locker a můžeme také přidat useState abyste to měli přehled.

import Orientation, { LANDSCAPE } from 'react-native-orientation-locker'
import { singletonHook } from 'react-singleton-hook'

export const useScreenOrientation = singletonHook(
  {
    isLandscape: false,
    screenOrientation: Orientation.getInitialOrientation(),
  },
  () => {
    const [screenOrientation, setScreenOrientation] = useState(Orientation.getInitialOrientation())

    return {
      isLandscape: screenOrientation.includes(LANDSCAPE),
      screenOrientation
    }
  },
)

Krok 3
OK. Nyní k hlavní části tohoto problému, musíme naslouchat změnám orientace obrazovky. Mám zde malou pomocnou funkci, kterou používám všude. Bude se to hodit kvůli dříve zmíněnému upozornění a jen mi to říká, zda jsme nebo nejsme na zařízení Android.

import { Platform } from 'react-native'

export const isAndroid = () => Platform.OS === 'android'

Níže nastavím useEffect který se spustí pouze jednou, protože nemá žádné závislosti, a poté nastavte dva posluchače, jeden pro iOS, který používá response-native-orientation-locker, a druhý pro Android, který používá posluchače událostí dimenzí ze samotného React Native (Nezapomeňte odebrat posluchači, když je háček zničen). V podstatě pak při změně orientace obrazovky nastavíme stav na správný OrientationType (NA VÝŠKU, NA VÝŠKU-Vzhůru DOLŮ, NA KRAJINU-VLEVO, NA KRAJINKU-VPRAVO). U Androidu jen zkontrolujeme výšku oproti šířce, abychom poznali, zda je to na výšku nebo na šířku. Samozřejmě, že když si to přečtete, tento reaktivní-nativní-orientační-locker funguje konzistentně pro Android, nebudete potřebovat žádný z tohoto specifického kódu Android.

import { useEffect, useState } from 'react'
import { Dimensions, ScaledSize } from 'react-native'
import Orientation, { LANDSCAPE, OrientationType } from 'react-native-orientation-locker'
import { singletonHook } from 'react-singleton-hook'


export const useScreenOrientation = singletonHook(
  {
    isLandscape: false,
    screenOrientation: Orientation.getInitialOrientation(),
  },
  () => {
    const [screenOrientation, setScreenOrientation] = useState(Orientation.getInitialOrientation())

     useEffect(() => {
      const onChange = (result: OrientationType) => {
        setScreenOrientation(result)
      }

      const onChangeAndroid = (result: { screen: ScaledSize }) => {
        return onChange(
          result.screen.height > result.screen.width
            ? OrientationType.PORTRAIT
            : OrientationType['LANDSCAPE-LEFT'],
        )
      }

      if (isAndroid()) {
        Dimensions.addEventListener('change', onChangeAndroid)
      } else {
        Orientation.addOrientationListener(onChange)
      }

      return () => {
        if (isAndroid()) {
          Dimensions.removeEventListener('change', onChangeAndroid)
        } else {
          Orientation.removeOrientationListener(onChange)
        }
      }
    }, [])

    return {
      isLandscape: screenOrientation.includes(LANDSCAPE),
      screenOrientation
    }
  },
)

Dotáhli jste to až do konce! 🎉

Děkuji za přečtení. Bylo to poprvé, co jsem se pokusil něco takového napsat. Řekněte mi, co by se podle vás mohlo zlepšit, a já se pokusím tato vylepšení začlenit do budoucích.