[Flutter] mountedとは？

# mountedとは？
Flutterでは、ウィジェットがマウントされているかどうかを確認するために 
`mounted` プロパティが提供されています。

このプロパティは、ウィジェットが現在のウィジェットツリーに含まれていて、
描画されているかどうかを確認するためのプロパティです。

特に、非同期処理やWidgetのライフサイクルを超えて操作を行う場合に使用されます。

Widgetがツリーに追加されると、`mounted`は`true`に設定され、
一方、Widgetがツリーから削除された場合(新しい画面に遷移したときや、
親Widgetが削除されたときなど)、`mounted`は`false`に設定されます。

`mounted` のチェックを行わない場合、例外が発生したり、
予期しない動作につながる可能性があるので、
WidgetのステートやUIを変更する操作を行う前には、
必ず `mounted` の値をチェックすることが重要です。

## 使用理由
`mounted` プロパティを使用する主な理由とメリットは以下の通りです。

### 1. Widgetのライフサイクルを適切に管理する
Widgetがウィジェットツリーから削除された後にステートを変更しようとするのを
防ぐことができます。これにより、不必要な例外やクラッシュを回避できます。

### 2. メモリリークを防ぐ
Widgetがアンマウントされた後に非同期処理が完了した場合、
その結果を処理しようとするとメモリリークが発生する可能性があります。
`mounted` プロパティを使用することで、不要な処理を行わずにリソースを管理できるため、
メモリリークを防ぐことにも繋がります。

### 3. パフォーマンスの向上
不要な処理を行わずに済むことで、アプリのパフォーマンスが向上します。
Widgetがアンマウントされた後に無駄な処理を行うことがなくなるため、
リソースを効率的に使用できます。

### 4. コードの堅牢性の向上
予期せぬ状況でのエラーを回避できます。これにより、コードの堅牢性が向上し、
安定したアプリケーションを構築できます。

### 5. デバッグの容易性
Widgetのライフサイクルに関するエラーを特定しやすくなります。
これにより、デバッグが容易になり、開発効率が向上します。

# 使用例

例えば、`StatefulWidget`でAPIからデータを非同期で取得する場合を考えてみましょう。
Widgetが `fetchData` メソッドの完了前にウィジェットツリーから削除された場合、
そのWidgetのステートを更新しようとするとエラーが発生する可能性があります。

このような問題を回避するために、WidgetのステートやUIを変更する前には、
必ず`mounted`の値をチェックする必要があります。

以下は、非同期処理で`mounted`を使用する例です。

```dart
// StatefulWidgetの中で使用される
Future<void> fetchData() async {
  final data = await someAsyncOperation();
  if (mounted) {
    setState(() {
      // 取得したデータでWidgetのステートを更新
      _data = data;
    });
  }
}
```

この例では、 `fetchData` メソッドが非同期処理( `someAsyncOperation` )を
実行しています。取得したデータでWidgetのステートを更新する前に、
`mounted` の値をチェックしています。

もしWidgetがアンマウント(別の画面に遷移したり削除された場合)されていれば、
`setState` の呼び出しをスキップし、エラーの発生を防いでいます。

`mounted` を使用する場面は以下のような場合が想定されます。
- WidgetのステートやUIを変更する前
- 非同期処理、タイマー、Widgetのライフサイクルを超えて操作を行う場合

# 使い方
以下は、 `mounted` プロパティを使用した例です。

```dart
class _MyWidgetState extends State<MyWidget> {
  void _handleCallback() {
    if (mounted) {
      setState(() {
        // ステートを更新する処理
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        // 別のWidgetからコールバック関数を呼び出す
        someOtherWidget.callbackFunction = _handleCallback;
      },
      child: const Text('Update State'),
    );
  }
}
```

この例では、ボタンを押すと別のWidgetからコールバック関数が呼び出されます。
コールバック関数内でステートを更新するため、 `mounted` のチェックが必要になります。

コールバック関数が呼び出された時点でWidgetがアンマウントされていた場合、
`mounted` チェックを行わずに `setState()` を呼び出すとエラーになってしまいます。
そのため、 `mounted` チェックを行った上でステートを更新する必要があります。

:::note info
`mounted` 判定を行わずに `setState()` を呼び出した場合、
"setState() or markNeedsBuild() called during build"というエラーが発生する
可能性があります。これは、ビルドフェーズ中にステートが
変更されたことを示すエラーです。
:::

# 注意点
:::note warn
`mounted` プロパティを使用する際には、ウィジェットが破棄された後に
アクセスされる可能性があるため、そのことを考慮する必要があります。
ウィジェットがマウントされていない場合にアクセスすると、
エラーが発生する可能性がありますので、適切にチェックすることが重要です。
:::

# まとめ
`mounted` プロパティを理解し、適切に使用することで、
Widgetのライフサイクルの変更を適切に処理し、アンマウントされたWidgetを
操作することによる問題を回避できるようになります。

特に非同期処理やWidgetのライフサイクルが複雑になる場合は、
`mounted` プロパティの使用が必須となります。これを機にぜひ使用してみてください。

# 参考資料

https://qiita.com/Yu_unI1/items/293a45d136ee47699deb

https://api.flutter.dev/flutter/widgets/State/mounted.html

https://zenn.dev/mukkun69n/articles/ec7badd30fca17

# 告知
最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを
募集しております。詳しくは採用情報ページをご確認ください。

https://eda-inc.jp/recruit/

みなさまからのご応募をお待ちしております。