[基礎知識]flutter_hooks/useMemoized関数

flutter_hooks/useMemoized関数

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

useMemoized 関数は、
複雑オブジェクト(計算コストが高い値インスタンスキャッシュ(メモ化)
します。そして、
・設定するキー値(依存配列)が変更された場合のみ、新しい値が再計算されます。
・それ以外の場合は、前回のキャッシュされた値が返されます。
 (逆に表現すると、計算結果が異なる場合でも、キー値(依存関係)の設定が無ければ、再キャッシュはされません

(定義)flutter_hooks/useMemoized関数:
T useMemoized<T>(
  T valueBuilder(
    //
  ), 
  [List<Object?> keys = const <Object>[]]
)

この関数は、

特定の計算が必オブジェクト
生成するための
valueBuilder 関数を受け取ります。

また、
オプションとして
keys リストを受け取る
ことができ、
この keys に基づいて
キャッシュ有効性
断されます。

1.使用

(1)初回呼び出し

useMemoized は、
最初呼び出し時
valueBuilder 関数呼び出し
その結果内部的キャッシュ
します

(例)初回のウィジェットのビルド時にインスタンスが生成され、キャッシュされます。:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class ExpensiveObject {
  final int id;
  final String name;

  ExpensiveObject({required this.id, required this.name});

  // 通常、もっと複雑な処理やリソースの消費が伴うことを想定
}

class ExpensiveWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // useMemoizedを使用してExpensiveObjectインスタンスをキャッシュ
    final expensiveInstance = useMemoized(() {
    
      return ExpensiveObject(id: 1, name: 'Very Expensive');
    });

    return Scaffold(
      appBar: AppBar(
        title: Text('Use Memoized Example'),
      ),
      body: Center(
        child: Text('Object ID: ${expensiveInstance.id}, Name: ${expensiveInstance.name}'),
      ),
    );
  }
}

コードの説明)
ExpensiveObject
 これはサンプルとして用意した複雑なオブジェクトを生成するクラスです。実際には、このクラスのインスタンス生成がリソースを多く消費するか、時間がかかる可能性があるため、キャッシュする価値があります。

useMemoized
 ExpensiveObjectインスタンスを生成する関数を渡しています。useMemoizedはこの関数を一度呼び出し、その結果をキャッシュします。ウィジェットが再ビルドされるたびに、キーが変わらない限り同じインスタンスを返します。

このコードは、初回呼び出し時にvalueBuilder関数が実行され、結果がキャッシュされるuseMemoizedの使用方法を示しています。再ビルド時には、新たなオブジェクト生成が行われず、キャッシュからデータが取得されるため、パフォーマンスが向上します。

(2)再ビルド

ウィジェット再ビルドされる際に useMemoized再度呼び出されるkeys 変更されていなければvalueBuilder 関数を再び呼び出すことなくキャッシュされた値返します

(例(キーが変更されていない場合)useMemoizedがキャッシュされた値を返します。:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class ExpensiveObject {
  final int id;
  final String name;

  ExpensiveObject({required this.id, required this.name});
}

class ExpensiveWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    // useMemoizedを使用してExpensiveObjectインスタンスをキャッシュ
    // キーが変わらない限り、キャッシュされた値を返す
    final expensiveInstance = useMemoized(() {
      return ExpensiveObject(id: 1, name: 'Very Expensive');
    });

    return Scaffold(
      appBar: AppBar(
        title: Text('Use Memoized Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Object ID: ${expensiveInstance.id}, Name: ${expensiveInstance.name}'),
            RaisedButton(
              child: Text('Rebuild Widget'),
              onPressed: () {
                // このボタンを押すとウィジェットが再ビルドされる
                (context as Element).markNeedsBuild();
              },
            ),
          ],
        ),
      ),
    );
  }
}

ウィジェットの再ビルド
 RaisedButton の onPressed メソッド内で (context as Element).markNeedsBuild(); を呼び出しています。これにより、ウィジェットが強制的に再ビルドされます。

useMemoizedの動作
 再ビルドが発生しても、ExpensiveObject の生成に使用されるキー(この場合は暗黙的に無し)が変更されていないため、useMemoizedはキャッシュされたインスタンスを再度返します。このため、ExpensiveObjectのコンストラクタは再ビルド時に再び呼び出されません。

この例は、ウィジェットのライフサイクル中に何度も発生する可能性のある再ビルドにおいて、計算コストが高いオブジェクトの生成を避けるためにuseMemoizedがどのように利用されるかを示しています。再ビルドが頻繁に発生しても、パフォーマンスの低下を防ぐために前回のビルドで生成された値を再利用します。

(3)キー変更

keys 前回呼び出しから変更されている場合は、valueBuilder 再び呼び出され、新しい値生成されてキャッシュされます。

(例(キーが変更されている場合)useMemoizedは、(nameが変わる度に)新規オブジェクトを生成。:
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class ExpensiveObject {
  final int id;
  final String name;

  ExpensiveObject({required this.id, required this.name});
}

class ExpensiveWidget extends HookWidget {

 final String name; // ウィジェットのプロパティとして名前を渡す
 ExpensiveWidget({Key? key, required this.name}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // useMemoizedを使用してExpensiveObjectインスタンスをキャッシュ
    // nameが変わるたびに新しいオブジェクトを生成
    final expensiveInstance = useMemoized(() {
      return ExpensiveObject(id: 1, name: 'Very Expensive');
    }, [name]); // name をキーとして使用

    return Scaffold(
      appBar: AppBar(
        title: Text('Use Memoized Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Object ID: ${expensiveInstance.id}, Name: ${expensiveInstance.name}'),
            RaisedButton(
              child: Text('Rebuild Widget'),
              onPressed: () {
                // このボタンを押すとウィジェットが再ビルドされる
                (context as Element).markNeedsBuild();
              },
            ),
          ],
        ),
      ),
    );
  }
}

このコードでは、nameがウィジェットのプロパティとして渡されています。これにより、nameが変更された場合にのみExpensiveObjectの新しいインスタンスが生成されます。nameが変更されない限り、useMemoizedは以前にキャッシュされたインスタンスを返し、不必要な再計算やリソース消費を避けることができます。

関連ThirdPartyブログ

function”>Zenn/useMemoized<T> function

コメントを残す