[React Native] Dimensions.get("window") には気を付けよう

3 min read読了の目安(約2800字

React Native では端末のウィンドウの大きさを取得する API がいくつかありますが、そのうちの一つが Dimensions.get です。
しかし、この API は注意して使わないと不具合を起こしてしまいます。

不具合が起きるケース

いきなりですが、画面の上部にヘッダー画像を表示したいとします。
例えばこのスクショのような。

そこで、以下のような実装でヘッダー画像を実現しました。

import React from 'react';
import { StyleSheet, Image, Dimensions, SafeAreaView } from "react-native";

export default function App() {
  return (
    <SafeAreaView>
      <Image
        source={{ uri: "https://example.com/header.jpg" }}
        style={styles.headerImage}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  headerImage: {
    width: Dimensions.get("window").width,
    height: 100,
  },
});

しかし、この実装方法には問題があり、特定のケースで 不具合が発生します

問題となる箇所は styles を定義しているところです。

const styles = StyleSheet.create({
  headerImage: {
    width: Dimensions.get("window").width,
    height: 100,
  },
});

不具合が発生するのは、 端末を回転させたとき です。

Dimensions.get("window") は、この関数が呼ばれた時点のウィンドウの大きさを返します。すなわち、 styles = StyleSheet.create(... が実行された時点で、headerImage.width の値が固定されてしまいます。

そのため、端末の回転で問題が発生します。
横向き状態のスクショを載せておきます。

作り手の意図としては、横向きの状態でもヘッダー画像が端末の幅いっぱいに表示されて欲しいのですが、みて分かる通り一部分にしか表示されていません。原因は、最初に縦向きで画面を表示した時に headerImage.width の値が縦向きの幅に固定されてしまい、横向きになった後も縦向き時の幅で描画されているからです。

実は React Native の公式ドキュメントにも以下のような記述があり、注意してね!と書いてあります。

Although dimensions are available immediately, they may change (e.g due to device rotation, foldable devices etc) so any rendering logic or styles that depend on these constants should try to call this function on every render, rather than caching the value (for example, using inline styles rather than setting a value in a StyleSheet).
https://reactnative.dev/docs/dimensions

Dimensions.get を使わない実装

最後に、意図通りに横向きでも幅いっぱいに表示する方法を紹介します。
主に2つの方法が有るかなと思います。

  • useWindowDimensions を使う
  • width: '100%' を指定する

useWindowDimensions を使う方法は RN ドキュメントの Dimensions の項目でも推奨されている方法です。

useWindowDimensions is the preferred API for React components. Unlike Dimensions, it updates as the window's dimensions update. This works nicely with the React paradigm.

コンポーネントの中で useWindowDimensions を呼べば、回転のたびにウィンドウの値が更新されて、常に正しい幅を取得することができます。

const windowWidth = useWindowDimensions().width;

もう一つの方法はスタイルで width: '100%' を指定する方法です。
昔の React Native では width のパーセント指定ができなかったらしいのですが、React Native ver 0.42 でパーセント指定が可能になっているので、現在(2021年)では width: '100%' が有効です。パーセント指定すれば、回転に応じた幅になります。

まとめ

以上、React Native の Dimensions.get には注意が必要という話でした。スマホ向けアプリではそもそも回転を許可しないことも多いと思いますが、タブレットでの利用を想定するなら回転できた方が良いケースも多いと思いますし、実装した時点では縦固定を想定していたとしても後々で回転を許可したいと方針が変わることもあるかと思います。
その際 Dimensions.get を使っていると問題が起こるので、個人的には Dimensions.get は使わず、代わりの API を使うことにします。