メインコンテンツまでスキップ

useFocusEffect

前セクションではToDo登録画面を追加しました。 しかし、現時点では登録したToDoがToDo一覧画面に表示されません。 ここではその不具合に対応します。

なぜ追加したToDoが表示されないのでしょうか。 次に示すのはToDo一覧画面のコード箇所です。 useEffectフックを使用して、画面マウント時にTodoService.getTodosを呼び出してToDo一覧を取得しています。

  useEffect(() => {
let isActive = true;
TodoService.getTodos()
.then(response => {
if (isActive) {
setTodos(response);
}
})
.catch(() => {});
return () => {
isActive = false;
};
}, []);

上記の場合、useEffectフックの処理は画面マウント時にしか実行されません。 ToDo登録画面が表示されている時も、ToDo一覧画面はスタックの下に隠れて生きてます。 そのため、(ToDo登録画面が破棄されて)ToDo一覧画面が再表示された場合、画面マウント時ではないためuseEffectフックの処理が動作しません。 それがToDo一覧画面にToDoが表示されない理由です。

今回の場合、画面にフォーカス移った(画面が再表示された)際にもToDo一覧を取得したいです。 このような場合のため、React NavigationではuseFocusEffectフックが用意されています。 useFocusEffectフックは画面フォーカス時に処理を実行します。

注記

詳細は、React Navigation公式ドキュメントのuseFocusEffectを参照してください。

では修正していきましょう。 src/screens/todo/TodoBoard.tsxを修正してください。

src/screens/todo/TodoBoard.tsx
- import {useNavigation} from '@react-navigation/native';
+ import {useNavigation, useFocusEffect} from '@react-navigation/native';
import {FilterType, TodoFilter, TodoList} from 'components/parts';
- import React, {useContext, useEffect, useState} from 'react';
+ import React, {useCallback, useContext, useState} from 'react';
import {Alert, StyleSheet, View} from 'react-native';
import {Icon, ThemeContext} from 'react-native-elements';
import {Todo, TodoService} from 'services';

/* ~省略~ */

export const TodoBoard: React.FC = () => {
const {theme} = useContext(ThemeContext);
const navigation = useNavigation();
const [todos, setTodos] = useState<Todo[]>([]);
const [filterType, setFilterType] = useState<FilterType>(FilterType.ALL);

- useEffect(() => {
+ useFocusEffect(
+ useCallback(() => {
let isActive = true;

TodoService.getTodos()
.then(response => {
if (isActive) {
setTodos(response);
}
})
.catch(() => {});

return () => {
isActive = false;
};
- }, []);
+ }, []),
+ );

/* ~省略~ */

コールバック関数をuseCallbackでラップしている点に注意してください。 useFocusEffectは、画面にフォーカスがある場合にのみ実行されるという点を除き、ReactのuseEffectフックに似ています。 最初のレンダリング時(画面にフォーカスがある場合)だけではなく、依存関係が変更された場合にも実行されます。 useCallbackでラップしないと、画面フォーカス時のすべてのレンダリングでコールバック関数が実行されてしまいます。 上記処理の場合、画面がState変更されたタイミングで再レンダリングが走るため、useCallbackでラップしないと無限に再レンダリングしてしまいます。

また、戻り値に関数を返している点にも注意してください。 これはクリーンアップ関数と呼ばれるもので、次の場合に実行されます。

  • 依存関係が変更されて新しい処理がスケジュールされた
  • 画面がアンマウントされた
  • 画面がフォーカスを失った

ここでは、上記場合に画面Stateを抑止する目的でクリーンアップ関数を活用しています。

注記

詳細は、React Native公式ドキュメントのRunning asynchronous effectsを参照してください。

修正できたら実行してください。 ToDo登録画面で登録したToDoがToDo一覧画面に表示できたら成功です。