flutter 即使没有任何更改,在使用冻结的copywith更新状态时,Riverpod也会触发重建

utugiqy6  于 2023-01-05  发布在  Flutter
关注(0)|答案(2)|浏览(162)

我以为Riverpod只会在状态值不同的情况下触发重建,但结果是每次设置状态时都会重建,尽管状态值是相同的。
情况如下

@Freezed(genericArgumentFactories: true)
class Model with _$Model {
  const factory Model({required int id}) = _Model;
}

class Manager {
  static StateProvider<Model> modelProvider =
      StateProvider<Model>((ref) => Model(id: 1));
  Manager() {
    Stream.periodic(Duration(seconds: 1)).take(1000).listen((event) {
      ref.read(modelProvider.notifier).update((state) {
        var cloneState = state.copyWith();
        print("${state == cloneState}"); //This print true
        return cloneState;
      });
    });
  }
}

class TestWidget extends ConsumerWidget {
  const TestWidget();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    var model = ref.watch(Manager.modelProvider);
    print("model change......................"); //print every second
    return Text(model.id.toString());
  }
}

它显示TestWidget每秒钟重建一次,但我认为它不应该,因为状态是相同的,尽管我再次设置了它。我错过了什么吗?谢谢。

3zwtqj6y

3zwtqj6y1#

这完全是在幕后使用identical(old, current)来比较状态。identical为自己提供了以下内容:

/// Check whether two references are to the same object.
///
/// Example:
/// ```dart
/// var o = new Object();
/// var isIdentical = identical(o, new Object()); // false, different objects.
/// isIdentical = identical(o, o); // true, same object
/// isIdentical = identical(const Object(), const Object()); // true, const canonicalizes
/// isIdentical = identical([1], [1]); // false
/// isIdentical = identical(const [1], const [1]); // true
/// isIdentical = identical(const [1], const [2]); // false
/// isIdentical = identical(2, 1 + 1); // true, integers canonicalizes
/// ```
external bool identical(Object? a, Object? b);

下面是一个完整的拷贝运行示例:

void main() => runApp(const ProviderScope(child: MyApp()));

@Freezed(genericArgumentFactories: true)
class Model with _$Model {
  const factory Model({required int id}) = _Model;
}

class Manager {
  static StateProvider<Model> modelProvider = StateProvider<Model>((ref) {
    Stream.periodic(const Duration(seconds: 1)).take(1000).listen((event) {
      ref.read(modelProvider.notifier).update((state) {
        final cloneState = state.copyWith();
        // const cloneState = Model(id: 1); //The print true in both cases
        print("${state == cloneState}"); //This print true
        print("identical: ${identical(state, cloneState)}"); //This print false
        return cloneState;
      });
    });

    return const Model(id: 1);
  });
}

class MyApp extends ConsumerWidget {
  const MyApp();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    var model = ref.watch(Manager.modelProvider);
    print("model change......................"); //print every second
    return MaterialApp(home: Text(model.id.toString()));
  }
}

我对这个例子做了一些修改,但本质保持不变。只有通过应用'const',我们才能避免重建。

dldeef67

dldeef672#

默认情况下,Riverpod不依赖==,而是依赖identical来过滤更新。
原因是,如果您的模型变得很大,==的效率会非常低。
但这会导致您描述的行为:如果两个对象具有相同的内容但使用不同的示例,则会通知侦听器。
不过,这并不被视为一个问题,因为这样做没有任何价值:

state = state.copyWith();

相关问题