ReactNative(Expo)のAndroid開発でつまづいた点


iOSで動作してもAndroidでエラーになることが多いのでメモ
※随時更新、重要そうなもの順

JSXで「0」で判定してはいけない

「!!」か比較演算子で判定する
参考: https://github.com/facebook/react-native/issues/18773

NG
{state.zero &&
    <Text>
        Some text
    </Text>
}
OK
{state.zero > 0 &&
    <Text>
        Some text
    </Text>
}
OK
{!!state.zero &&
    <Text>
        Some text
    </Text>
}

Textの中にViewはエラー

NG
<Text>
  <View></View>
</Text>

たぶんこれをしたい時は横並びにしたい時なのでflexDirectionを使う

OK
<View style={{ flexDirection: 'row' }}>
  <View></View>
  <View></View>
</View>

AdMobの「Interstitial、Rewarded」広告を閉じるとクラッシュする

ExpoのAdMobの場合
componentDidMount() {
  AdMobInterstitial.addEventListener('interstitialDidClose', () => {
    this.props.navigation.navigate('Home')
  })
}

range関数的なもの

参考: jsでrange関数をつくる

  • 動作する
    • Array.from({length: 5}, (v, k) => k);
    • Array.from(Array(5), (v, k) => k)
    • ...Array(5)
  • 動作しない。空配列になる(iOSだと動作する)
    • ...Array(5).keys()

splash screen

動作しないわけではないが、Android用のちょうど良い大きさが作れないため背景色と画像の色を同じにして対応するしかない
(追記:iPhoneの違うサイズでも同じことになりました)

NativeBase関連

Thumbnailで丸角にならない

参考: https://github.com/facebook/react-native/issues/7124

NG
<Thumnail source={ require('./logo.png')) } />
OK
<View style={{
  borderRadius: 50,
  overflow: 'hidden',
}}>
  <Thumnail source={ require('./logo.png')) } />
</View>

フォントがないエラー

App開始時に読み込む

import { Font } from 'expo'

export const fonts = [
  { Roboto: require('native-base/Fonts/Roboto.ttf') },
  { Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf') },
  { Ionicons: require('@expo/vector-icons/fonts/Ionicons.ttf') }
]

fonts.map(font => Font.loadAsync(font))

Expo Audioで音を何度も再生する

https://github.com/expo/expo/issues/915
iOSは修正されていましたが、Androidでは修正されていないようでした(Expo SDK v.27ではダメでした)
Expo.Audio.Sound.createとか色々試しましたがダメでした

結局、上のissuesに記述されている https://snack.expo.io/HyX29l71z のsound.jsをそのまま使うことで動作しました(エラーはでますが...)
念のためplayのとこにtry&catch

sound.js
import Expo from 'expo'

const SOUNDS = {}
let SOURCES = {}

export async function prepareSound() {
  await Expo.Audio.setIsEnabledAsync(true)
  await Expo.Audio.setAudioModeAsync({
    playsInSilentModeIOS: false,
    allowsRecordingIOS: false,
    interruptionModeIOS: Expo.Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
    shouldDuckAndroid: false,
    interruptionModeAndroid: Expo.Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX
  })
}

export function loadSounds(sources) {
  SOURCES = { ...SOURCES, ...sources }
}

export async function playSound(key) {
  try {
    if (SOUNDS[key]) {
      await SOUNDS[key].unloadAsync()
    } else {
      SOUNDS[key] = new Expo.Audio.Sound()
    }
    await SOUNDS[key].loadAsync(SOURCES[key])
    if (key.indexOf('loop') !== -1) await SOUNDS[key].setIsLoopingAsync(true)
    SOUNDS[key].playAsync()
  } catch (err) {
    // console.log(err)
  }
}

export async function stopSound(key) {
  try {
    SOUNDS[key].stopAsync()
  } catch (err) {
    // console.log(err)
  }
}