ことの発端
Expoで作ったアプリがフォアグラウンドになったタイミング、すなわちiOSアプリでいうところのviewWillEnterForegroundをどう書くのか、React Nativeのドキュメントで調べたところ、
import { useRef, useState, useEffect } from 'react'; import { AppState, AppStateStatus } from 'react-native'; const appState = useRef(AppState.currentState); const [appStateVisible, setAppStateVisible] = useState(appState.current); useEffect(() => { const subscription = AppState.addEventListener('change', (nextAppState: AppStateStatus) => { if (appState.current.match(/inactive|background/) && nextAppState === 'active') { // フォアグラウンドになった際の処理を書く } appState.current = nextAppState; setAppStateVisible(appState.current); }); return () => { subscription.remove(); }; }, []);
という感じで書けばOKということがわかりました。
早速、Expoでつくったアプリに導入してみたところ、ここの部分にエラーが出ました。
setAppStateVisible(appState.current); // → Property 'remove' does not exist on type 'void'.
AppState.addEventListenerの戻り値がvoidなことが原因です。
原因と対策
参照していたドキュメントのバージョンが0.67のもので、Expoで使っているreact-nativeが0.64.3だったことが原因でした。
改めて0.63のドキュメントを確認すると、
import { useEffect, useRef } from 'react'; import { AppState, AppStateStatus } from 'react-native'; const appState = useRef(AppState.currentState); useEffect(() => { AppState.addEventListener('change', handleAppStateChange); return () => AppState.removeEventListener('change', handleAppStateChange); }, []); function handleAppStateChange(nextAppState: AppStateStatus) { if (appState.current.match(/inactive|background/) && nextAppState === 'active') { // フォアグラウンドになった際の処理を書く } appState.current = nextAppState; }
と、AppState.addEventListenerの戻り値のremoveではなく、AppState.removeEventListenerを叩いています。
そして、こちらの書き方で問題なく動作しました。
変更されたタイミングを知るべく、順番に確認していくと、どうやら0.65で変更が入った模様です。
さらに調査を進めると、そもそもExpoのドキュメントにあっさりと答えが書いてありました。なんてこった。
https://docs.expo.dev/versions/latest/react-native/appstate/docs.expo.dev
結論
Expoが最新のReact Nativeを使っているとは限らない、というか開発元が違うのでほぼほぼ古いバージョンを使っているはずなので、React NativeのドキュメントよりもExpoのドキュメントを調べた方が良い。React Nativeのドキュメントで調べるときはバージョンに注意。というごくごく当たり前のことを改めて心に刻みました。