最近React Nativeでアプリを作っていたのですが、奇妙なバグに出くわしたのでメモ書き程度に残しておきます。
開発環境
開発環境はExpoです。
{
(省略)
"dependencies": {
"expo": "~44.0.0",
"expo-linear-gradient": "^11.0.3",
"expo-status-bar": "~1.2.0",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-native": "0.64.3",
"react-native-web": "0.17.1"
},
"devDependencies": {
"@babel/core": "^7.12.9",
"@types/react": "~17.0.21",
"@types/react-native": "~0.64.12",
"typescript": "~4.3.5"
}
}
スクロールすると落ちる
とある条件下でスクロールビューを動かすと稀にアプリが落ちるという不具合です。
初めてこのバグに気付いた時は普通にスクロールしただけでアプリが落ちたのですが、色々試行錯誤した結果、動画のようにpadding(またはmargin)を設定している画面端でマウスをドラッグしたままクルクルすると再現しやすいことがわかりました。(なんでかはわかりません)
実機検証中にこのバグが発生したので、シミュレータでも再現するか確認してみましたが、再現はしたもののエラーログも何も吐かずにアプリがクラッシュして途方に暮れていました。
再現させるために色々試して気付いたことは
- スクロールできる状態でないと再現しない
- ScrollView内のコンテンツがScrollViewに全て収まる状態でオーバースクロールしても再現しない
- iOSのみ再現する
ということです。
問題のコード
バグが再現するコードです。
import { SafeAreaView, ScrollView, StyleSheet, Text } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
export default function App() {
return (
<SafeAreaView style={styles.container}>
<LinearGradient
style={styles.linearGradient}
colors={['red', 'yellow']}>
<ScrollView>
<Text>
{省略}
</Text>
</ScrollView>
</LinearGradient>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
linearGradient: {
width: '100%',
height: '100%',
paddingLeft: 30
}
});
ExpoのログではなくOS側のログを見てもそれらしきエラーログを発見できなかったので、仕方なくどういう条件でバグが再現するのかをしらみ潰しに試していきました。
結論としては、LinearGradientの中にScrollViewを置いている状態でスクロールするとアプリがクラッシュすることがあるようです。
解決策
解決策はシンプルで、LinearGradientにposition: 'absolute'
を指定して、LinearGradientの中にコンポーネントをネストさせないようにしました。
import { SafeAreaView, ScrollView, StyleSheet, Text } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
export default function App() {
return (
<SafeAreaView style={styles.container}>
<LinearGradient
style={styles.linearGradient}
colors={['red', 'yellow']}/>
<ScrollView>
<Text>
{省略}
</Text>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center'
},
linearGradient: {
width: '100%',
height: '100%',
position: 'absolute',
bottom: 0
}
});
終わりに
かれこれ半年近くReact Nativeをやってきましたが、腑に落ちないようなバグが多い印象があります。
スクロールビューにグラデーションをかけたいというニッチな(?)使い方をする人は少ないかと思いますが、参考になったら嬉しいです。
結局なぜアプリがクラッシュするのかわからないことだけが心残りですが...。(わかる方がいたら教えてください)