Coder Social home page Coder Social logo

MobX Store and Observable about brick HOT 2 CLOSED

getdutchie avatar getdutchie commented on July 17, 2024
MobX Store and Observable

from brick.

Comments (2)

tshedor avatar tshedor commented on July 17, 2024

Hey @HudsonAfonso thanks for the question. To be upfront, I've never used MobX in a Flutter project. I just went through the README and to answer your question, yes. However, how automagic the integration is will vary.

From the example in the README, we can add a Brick model to your store model:

// OfflineFirstWithRest is used here as it's the only Brick domain as of writing, but this will certainly expand to other domains like OfflineFirstWithFirebase or OfflineFirstWithGraphQl
@ConnectOffflineFirstWithRest() 
class ContactBase extends OfflineFirstWithRestModel with Store {
}

And that's it. The model will generate an adapter and fetch whenever you make calls to and from the repository.

// when changing values, the MobX store AND the repository have to be explicitly called

// updates observers but not the remote provider
contactBaseInstance.firstName = 'Thomas';

// to sync with the provider, we need to notify the repository
repository.upsert<ContactBase>(contactBaseInstance);

Since this isn't quite the automagic syncing that we've come to know and expect, we have to add an observer. Again, I have not used MobX and this may be an antipattern or cause performance issues:

// UNTESTED CODE
class Contact extends OfflineFirstWithRestModel with Store {
  Observable _instanceObserver;
  ReactionDisposer _instanceDisposer;

  Contact() {
    _instanceObserver = Observable(this);
   _instanceDisposer = reaction((_) => _observer.value, (instance) => MyRepository().upsert<Contact>(instance));
   // TODO exec _instanceDisposer.dispose();
  }
}

Alternatively, you could make your repository reactive. This would circumvent MobX entirely and would [essentially] make all fields @observed - any changes will trigger a rerender to the widget. As this requires a little more retrofitting to your existing app, it may be unideal.

class MyReactiveRepository extends OfflineFirstWithRestRepository {
  ... // constructor/super stuff here
  
  /// All streams by model and query
  /// Unique queries will generate unique streams
  final _streams = Map<Type, Map<Query, StreamController>>();

  /// Generate a new stream controller for per type per query
  /// Only generates if the type and query **do not** exist.
  void _addStreamController<_Model extends OfflineFirstWithRestModel>(Query query) {
    _streams[_Model] ??= Map<Query, StreamController>();
    _streams[_Model][query] ??= StreamController<List<T>>();
  }

  /// Listen to models as they're delivered
  Stream<List<_Model>> streamGet<_Model extends OfflineFirstWithRestModel>({query}) {
    _addStreamController<_Model>(query);
    super.get<_Model>(query: query).then((results) => _streams[_Model][query].add(results));
    
    return _streams[_Model][query].stream;
  }

  /// Add new instances to the observed [_streams]
  @override
  Future<_Model> upsert<_Model extends OfflineFirstWithRestModel>(instance, {query}) async {
    final result = await super.upsert<_Model>(instance, query: query);
    _addStreamController<_Model>(query);
    _streams[_Model][query].add(result);
    return result;
  }

  /// Close the stream of [query] if provided; otherwise, close all streams of [_Model]
  Future<void> close<_Model extends OfflineFirstWithRestModel>({Query query}) {
    if (query == null) {
      for(final map in _streams[_Model]) {
        map.values.forEach((v) => v.close());
      }
    }

    // safely navigate
    (_streams[_Model] ?? {})[query]?.close();
  }
}

/// example model
class User extends OfflineFirstWithRestModel {}

/// finally, render:
StreamBuilder<User>(
  stream: MyReactiveRepository().streamGet<User>(), 
  builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
    final models = snapshot.data;
  },
)

// later in the widget, you may want to close the streams you've created by this widget
// however, if the same data is rendered between multiple widgets, 
// the stream may be better left open at the risk of leaky memory
@override
void dispose() {
  super.dispose();
  MyReactiveRepository().close<User>();
}

from brick.

tshedor avatar tshedor commented on July 17, 2024

Hey @HudsonAfonso I'm going to close this out. Please reach out again if you have any more questions.

from brick.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.