mobxjs / mobx.dart

MobX for the Dart language. Hassle-free, reactive state-management for your Dart and Flutter apps.
https://mobx.netlify.app
MIT License
2.4k stars 310 forks source link

[BUG] Reaction won't trigger on an Object (With == Operator & hash code) with observable fields #837

Closed DattatreyaReddy closed 2 years ago

DattatreyaReddy commented 2 years ago

Problem: When i write a reaction on a complex data class with (==) operator and Hash-code, the reaction is not getting triggered.

After some digging with equals function in reaction I found that the object is updating itself even before comparing (like the fields of the previous Value are == current value). This seems wrong (or maybe i did messed up something). Please Help me!.


import 'package:json_annotation/json_annotation.dart'; import 'package:mobx/mobx.dart';

import 'enum/tnc_type.dart';

part 'invoice_tnc_model.g.dart';

@JsonSerializable() class InvoiceTnc extends InvoiceTncBase with $InvoiceTnc { InvoiceTnc({ super.categoryId, super.id, super.isSelected, super.organisationId, super.termAndCondition, super.tncType, });

factory InvoiceTnc.fromJson(Map<String, dynamic> json) => $InvoiceTncFromJson(json); Map<String, dynamic> toJson() => $InvoiceTncToJson(this); }

abstract class _InvoiceTncBase with Store { @observable String? categoryId; @observable String? id; @observable bool? isSelected; @observable String? organisationId; @observable String? termAndCondition; @observable @JsonKey( fromJson: TncType.fromJson, toJson: TncType.toJson, ) TncType? tncType; _InvoiceTncBase({ this.categoryId, this.id, this.isSelected, this.organisationId, this.termAndCondition, this.tncType, });

@override String toString() { return '_InvoiceTncBase(categoryId: $categoryId, id: $id, isSelected: $isSelected, organisationId: $organisationId, termAndCondition: $termAndCondition, tncType: $tncType)'; } @override bool operator ==(Object other) { if (identical(this, other)) return true;

return other is _InvoiceTncBase &&
    other.categoryId == categoryId &&
    other.id == id &&
    other.isSelected == isSelected &&
    other.organisationId == organisationId &&
    other.termAndCondition == termAndCondition &&
    other.tncType == tncType;

}

@override int get hashCode { return categoryId.hashCode ^ id.hashCode ^ isSelected.hashCode ^ organisationId.hashCode ^ termAndCondition.hashCode ^ tncType.hashCode; } }


- This is my reaction
```dart
      reaction<Invoice>(
        (_) => invoiceStore.currentInvoiceTnc,
        (_) {
          currentInvoice.currentInvoiceTnc.termAndCondition = "Hello";
          currentInvoice.currentInvoiceTnc.id = "Hi;
        },
      ),

This is my action

  @action
  Future<void> submit() async {
    invoiceStore.currentInvoiceTnc
    ..categoryId = "ds"
    ..id ="sd"
    ..isSelected =true
    ..organisationId = "HI";
  }

Ive edited the fields so that it is easy and short.

Thanks


Another Question:

This is the Dilemma that i have been facing. should i declare my data classes as final vars without stores or use stores with data class. ( Cause we don't exactly adding any new performance weight by declaring them as stores ) .

amondnet commented 2 years ago

@DattatreyaReddy

Use currentInvoiceTnc.hashCode for reaction.

abstract class _MainStore with Store {
  @observable
  InvoiceTnc currentInvoiceTnc = InvoiceTnc();
}
  @action
  Future<void> submit() async {
    invoiceStore.currentInvoiceTnc
    ..categoryId = "ds"
    ..id ="sd"
    ..isSelected =true
    ..organisationId = "HI";
  }
      reaction<Invoice>(
        (_) => invoiceStore.currentInvoiceTnc.hashCode,
        (_) {
          currentInvoice.currentInvoiceTnc.termAndCondition = "Hello";
          currentInvoice.currentInvoiceTnc.id = "Hi;
        },
      ),
DattatreyaReddy commented 2 years ago

@DattatreyaReddy

Use currentInvoiceTnc.hashCode for reaction.

abstract class _MainStore with Store {
  @observable
  InvoiceTnc currentInvoiceTnc = InvoiceTnc();
}
  @action
  Future<void> submit() async {
    invoiceStore.currentInvoiceTnc
    ..categoryId = "ds"
    ..id ="sd"
    ..isSelected =true
    ..organisationId = "HI";
  }
      reaction<Invoice>(
        (_) => invoiceStore.currentInvoiceTnc.hashCode,
        (_) {
          currentInvoice.currentInvoiceTnc.termAndCondition = "Hello";
          currentInvoice.currentInvoiceTnc.id = "Hi;
        },
      ),

I'll try. Thank you

Also, about my second question? is it use full to use store on data class?

DattatreyaReddy commented 2 years ago

@amondnet can you help me with the second Question

Another Question:

The one instant solution i could think of is to make the data class vars final and assign new var with copy-with function every time This is the Dilemma that i have been facing. should i declare my data classes as final vars without stores or use stores with data class. ( Cause we don't exactly adding any new performance weight by declaring them as stores ) .

amondnet commented 2 years ago

@DattatreyaReddy I prefer to use immutable data class. However, this may vary depending on the project.

fzyzcjy commented 2 years ago

Btw for immutable classes to auto generated hashCode and equals, I personally use freezed