useFocusEffect
画面にフォーカスが当たったときに副作用を実行したい場合があります。副作用には、イベントリスナーの追加、データの取得、ドキュメントタイトルの更新などが含まれる場合があります。これは`focus`イベントと`blur`イベントを使用して実現できますが、あまり人間工学的ではありません。
これを容易にするために、ライブラリは`useFocusEffect`フックをエクスポートします。
import { useFocusEffect } from '@react-navigation/native';
function Profile({ userId }) {
const [user, setUser] = React.useState(null);
useFocusEffect(
React.useCallback(() => {
const unsubscribe = API.subscribe(userId, (user) => setUser(user));
return () => unsubscribe();
}, [userId])
);
return <ProfileContent user={user} />;
}
エフェクトが頻繁に実行されないようにするには、例に示すように、`useFocusEffect`に渡す前にコールバックを`useCallback`でラップすることが重要です。
`useFocusEffect`は、Reactの`useEffect`フックに似ています。唯一の違いは、画面に現在フォーカスがある場合にのみ実行されることです。
このエフェクトは、`React.useCallback`に渡された依存関係が変更されるたびに実行されます。つまり、初期レンダリング時(画面にフォーカスがある場合)と、依存関係が変更された場合の後続のレンダリング時に実行されます。エフェクトを`React.useCallback`でラップしない場合、画面にフォーカスがある場合は、エフェクトはレンダリングごとに実行されます。
クリーンアップ関数は、前のエフェクトをクリーンアップする必要があるときに実行されます。つまり、依存関係が変更されて新しいエフェクトがスケジュールされたとき、画面がアンマウントまたはぼやけたときです。
非同期エフェクトの実行
サーバーからデータを取得するなどの非同期エフェクトを実行する場合、クリーンアップ関数でリクエストをキャンセルすることが重要です(`React.useEffect`と同様)。キャンセルメカニズムを提供しないAPIを使用している場合は、状態の更新を無視してください。
useFocusEffect(
React.useCallback(() => {
let isActive = true;
const fetchUser = async () => {
try {
const user = await API.fetch({ userId });
if (isActive) {
setUser(user);
}
} catch (e) {
// Handle error
}
};
fetchUser();
return () => {
isActive = false;
};
}, [userId])
);
結果を無視しない場合、API呼び出しの競合状態により、データに一貫性がなくなる可能性があります。
トランジションが完了するまでエフェクトを遅延させる
`useFocusEffect`フックは、画面にフォーカスが当たるとすぐにエフェクトを実行します。これは多くの場合、画面変更のアニメーションがある場合、まだ完了していない可能性があることを意味します。
React Navigationはネイティブスレッドでアニメーションを実行するため、多くの場合問題ありません。ただし、エフェクトがUIを更新したり、負荷の高いレンダリングを行う場合、アニメーションのパフォーマンスに影響を与える可能性があります。このような場合は、`InteractionManager`を使用して、アニメーションまたはジェスチャが完了するまで作業を延期できます。
useFocusEffect(
React.useCallback(() => {
const task = InteractionManager.runAfterInteractions(() => {
// Expensive task
});
return () => task.cancel();
}, [])
);
`useFocusEffect`は`focus`イベントのリスナーを追加することとどう違うのか
`focus`イベントは、画面にフォーカスが当たったときに発生します。イベントであるため、イベントをサブスクライブしたときに画面に既にフォーカスが当たっていた場合、リスナーは呼び出されません。また、画面のフォーカスが外れたときにクリーンアップ関数を実行する方法も提供されません。`blur`イベントをサブスクライブして手動で処理できますが、面倒になる可能性があります。通常、これらのイベントに加えて`componentDidMount`と`componentWillUnmount`も処理する必要があるため、さらに複雑になります。
`useFocusEffect`を使用すると、フォーカス時にエフェクトを実行し、画面のフォーカスが外れたときにクリーンアップできます。また、アンマウント時のクリーンアップも処理します。依存関係が変更されるとエフェクトが再実行されるため、リスナーの古い値について心配する必要はありません。
代わりに`focus`イベントと`blur`イベントを使用する場合
`useEffect`と同様に、`useFocusEffect`のエフェクトからクリーンアップ関数を返すことができます。クリーンアップ関数は、エフェクトをクリーンアップすることを目的としています。たとえば、非同期タスクを中止したり、イベントリスナーから登録解除したりするなどです。`blur`で何かを行うために使用することを意図したものではありません。
たとえば、**以下のことはしないでください**
useFocusEffect(
React.useCallback(() => {
return () => {
// Do something that should run on blur
};
}, [])
);
クリーンアップ関数は、エフェクトをクリーンアップする必要があるたびに実行されます。つまり、`blur`、アンマウント、依存関係の変更などです。状態を更新したり、`blur`で発生するはずのことを行うのに適した場所ではありません。代わりに`blur`イベントをリッスンする必要があります。
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
// Do something when the screen blurs
});
return unsubscribe;
}, [navigation]);
同様に、画面にフォーカスが当たったとき(画面フォーカスの追跡など)に何かを実行したいが、クリーンアップや依存関係の変更時に再実行する必要がない場合は、代わりに`focus`イベントを使用する必要があります。
クラスコンポーネントで使用する
エフェクトのコンポーネントを作成し、クラスコンポーネントで使用できます。
function FetchUserData({ userId, onUpdate }) {
useFocusEffect(
React.useCallback(() => {
const unsubscribe = API.subscribe(userId, onUpdate);
return () => unsubscribe();
}, [userId, onUpdate])
);
return null;
}
// ...
class Profile extends React.Component {
_handleUpdate = (user) => {
// Do something with user object
};
render() {
return (
<>
<FetchUserData
userId={this.props.userId}
onUpdate={this._handleUpdate}
/>
{/* rest of your code */}
</>
);
}
}