[基礎知識]flutter_hooks/useEffect関数

flutter_hooks/useEffect関数

flutter_hooks/useEffect関数(詳細は、pub.dev 参照)についての説明です。

1.useEffect の概要

useEffect 関数は、
副作用(side-effects)の管理
と、
必要応じてそのキャンセルを行う
ために使用されます。

2.動作詳細

(定義1(一般的表記))flutter_hooks/useEffect関数:
//useEffect はデフォルトで毎回ウィジェットのビルド時に同期的に呼び出されます。
void useEffect(
 Dispose? effect(
   //ここにオプション関数を返すことができる。
 ), 
 //keys を指定した場合は、keys 内の値が変更時のみ呼び出される。
 [List<Object?>? keys] 
)
(定義2(例文にすると))flutter_hooks/useEffect関数:
⓷ useEffect(() {
  ⓸ final subscription = stream.listen(print); //主処理
     //⓵ウィジェットの破棄時またはコールバックが再び呼び出される時に、
     // 購読をキャンセルする
     ⓹ return subscription.cancel; //オプション関数
   },
   //⓶Stream が変わると、useEffect はコールバックを再び呼び出す
   ⓺ [stream], //依存配列
);

シナリオ例:
1-1.アプリ起動時
 widget初回ビルド時、⓸(主処理)実行(stream購読開始)する。
 (この時点で、⓹(オプション)は実行されない。)
  ※オプションは、useEffect再実行前またはwidget破棄時に実行されるものです。
1-2.Firestoreからのstreamが変化したら・・・
 ⓺(依存配列)を根拠にして、まず⓹(オプション(cancel))実行する。

  ※ここまでが1セットの処理、となります。

2.そして、その後で、⓷(useEffect)再実行され、⓸(主処理(購読))開始される。

3.外部要因によるwidget再ビルド発生するも・・・
  (⓺(stream)変更が無い限り・・・)
   ⓷(useEffect)再実行はされません。
    (従って、⓸(主処理)⓹(オプション)とも実行されません。)

3.用途

  1. 副作用実行管理
  2. 副作用キャンセル処理のオプション提供

4.ポイント(復唱)

(1)主処理と依存配列(キー)

  1. useEffect デフォルトで、毎回ウィジェットのビルド時同期的呼び出されます
    ただし、keys 指定されている場合は、keys 内の変更されたときのみ呼び出されます。)

(2)オプション

  1. 引数として渡された effect コールバックは同期的に実行され、この effect からはオプションとして関数を返すことができます。この返された関数は、effect 再び呼び出されるか、ウィジェット破棄された時に呼び出されます

5.サンプル

5-1.initState の代わりに使ってみた

(main.dart)
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:test05/widgets/Animations/animation_example.dart';

void main() {
  runApp(
    const ProviderScope(
      child: MaterialApp(
        home: AnimationExample(
          duration: Duration(seconds: 15)
        ),
      ),
    ),
  );
}
(animation_hooks_exaample.dart)アニメーションを1度だけ開始する:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class AnimationExample extends HookWidget {
  const AnimationExample({Key? key, required this.duration})
      : super(key: key);

  final Duration duration;

  @override
  Widget build(BuildContext context) {
    //useAnimationController を使ってコントローラーを作成
    final controller = useAnimationController(duration: duration);

    //アニメーション開始処理(initState の代わりに useEffect で行う)
    useEffect(() {
      controller.forward(); //初期表示時にアニメーション開始
      return null; //クリーンアップ処理が不要なため null を返す
    }, []); //空のリストを渡すことで初回のビルド時のみ実行

    //duration が変更された場合にアニメーションを再スタートする
    useEffect(() {
      controller.duration = duration;
      controller.forward(); //新規 duration でアニメーション再開
      return null;
    }, [duration]); //duration が変更されたときにのみ再実行

    //アニメーションのビルド処理
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        return Opacity(
          opacity: controller.value, //透明度が0から1へと変化
          child: Container(
            width: 100 + 200 * controller.value,//幅(100→300)
            height: 100 + 200 * controller.value, //高(〃)
            color: const Color.fromARGB(255, 243, 33, 103),
          ),
        );
      },
    );
  }
}

1.初期化時のアニメーション開始:
useEffect を空のリスト [] とともに使用することで、初回のビルド時にのみ controller.forward() を呼び出しています。これは initState() で行っていた処理に相当します。

2.duration の変更時にアニメーションを再スタート:
useEffect の依存関係に duration を渡すことで、duration が変更された場合に controller.forward() を再び呼び出します。これは didUpdateWidget() で行っていた処理に相当します。

このように、flutter_hooks を使うことで、StatefulWidget を使わずに、シンプルかつ直感的にアニメーションの制御を行うことができます。この実装なら、コードの可読性とメンテナンス性が向上しますよね。
起動時:真っ黒
15秒後(duration):赤色に遷移

上記では、依存配列(キー)に[] を設定(つまり未設定と同じ)しているので、最初に1度だけ主処理が実行されます。
この動きは、initState() と同じ、といえます。

5-2.

以下の例では、useEffect を使用して Stream に購読し、
ウィジェットが破棄された時購読をキャンセルする処理を行います。また、
Stream が変更された場合以前の Stream の購読をキャンセルし、新しい Stream に購読を開始します。

実装

useEffect の内部実装では、_EffectHook を使い、指定された effect と keys をフックとして設定しています。
これにより、Flutter のウィジェットライフサイクル内で効率的に副作用を扱うことが可能です。

この関数は、Reactのフックにインスピレーションを受けたもので、Flutterでも効果的な状態管理とライフサイクルの管理を実現します。

_EffectHook とは何か?


_EffectHook は、flutter_hooks ライブラリにおいて、副作用を管理するための内部クラスです。このクラスは、副作用 (effect) とそれに関連する依存性のリスト (keys) を保持し、適切なタイミングで副作用をトリガーし、必要に応じてクリーンアップ(副作用の解除)を行います。

useEffect の実装
useEffect 関数はこの _EffectHook を使用して、副作用をウィジェットのライフサイクルと統合します。具体的には、useEffect は以下のステップで機能します:

副作用の定義: 関数として渡された effect が、ウィジェットがビルドされるたび、または指定された keys の値が変更されたときに実行されるように設定されます。
クリーンアップ関数の管理: effect 関数がクリーンアップ関数を返した場合、このクリーンアップ関数は副作用が再度実行される前やウィジェットが破棄される際に呼び出されます。これにより、リソースの解放やイベントリスナーの解除などが適切に行われます。
副作用の実行管理: _EffectHook は、副作用が正しいタイミングで実行され、必要な依存性に基づいて再実行されるよう管理します。
このように、_EffectHook は副作用の実行とそのライフサイクル管理の核心部分を担い、useEffect 関数を通じて開発者が直感的に副作用を扱えるように橋渡しをしています。

関連ThirdPatyブログ(とても参考になりました<(_ _)>のでご紹介致します)

Zenn/Flutter Hooksあれこれ(著者:わいすけさん)
useEffectはinitStateと同じなのか?

コメントを残す