brianegan / flutter_redux

A library that connects Widgets to a Redux Store
MIT License
1.65k stars 219 forks source link

ViewModel: Function fields vs function literals? #214

Open hacker1024 opened 3 years ago

hacker1024 commented 3 years ago

In many examples (including the TODO architecture sample), ViewModels are created like so:

class ViewModel {
  final void Function() myFunction;
  final String myProperty;

  ViewModel({
    required this.myFunction,
    required this.myProperty,
  });

  factory ViewModel.fromStore(Store<AppState> store) {
    return ViewModel(
      myFunction: () => store.dispatch(const MyFunctionAction()),
      myProperty: store.state.myProperty,
    );
  }
}

This (IMO) has the following problems

Why is that the recommended approach, compared to something like this?

class ViewModel {
  final Store<AppState> _store;

  ViewModel(this._store) : myProperty = _store.state.myProperty;

  final String myProperty;

  void myFunction() => _store.dispatch(const MyFunctionAction());
}
brianegan commented 3 years ago

Agreed -- your approach is a bit better for the reasons you state!

I was originally playing around with some ideas of creating the ViewModel more in the build function, where it's nice to be able to define those callback functions.

However, as I began to realize it's actually nicer to create the view Model in a static factory / normal class constructor, it now makes less sense.

bigbn commented 1 month ago

I'm using Equatable libaray for comparsion rules. The real-word example below:

class _StoreMediator extends Equatable {
  final String phoneNumber;
  final void Function() openAccountScreen;
  final void Function() openRegistrationScreen;

  @override
  List<Object?> get props => [phoneNumber];         //  <---

  const _StoreMediator(
      {required this.phoneNumber,
      required this.openAccountScreen,
      required this.openRegistrationScreen});

  static _StoreMediator connect(Store<RootState> store) {
    return _StoreMediator(
      phoneNumber: store.state.account.phoneNumber,
      openAccountScreen: () => store.dispatch(NavigateToAction('/account')),
      openRegistrationScreen: () => store.dispatch(NavigateToAction('/registration')),
    );
  }
}

this will ensure that only [phoneNumber] fields are compared