I have a Firestore document that changes infrequently and is used as a lookup table in many different places in my app. It is my understanding that when I do a get for the document it is always performing a server query, even if the data in the document hasn't changed. The local cache is only used when the server is unavailable.
Since the document changes infrequently I would like to do something like this...
1) When the app starts, get the document and store it locally.
2) Setup a listener so that if the document changes the local copy is updated.
3) When the local copy is updated broadcast this change to any widget that may be using the document.
This is the way I wish Firestore worked by default.
Is this a good idea? Any suggestions on how to implement this?
Here is what I ended up doing. It was actually fairly straight forward. I implemented this as a static class and not sure if that's the best approach. But I like everything else.
I created a class that sets up a listener for the document and also provides a stream for when it is updated. As a bit of side work my class also parses the document into a Map and sorts the games.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:async';
import 'package:pari/game.dart';
class Schedule {
static Map<String, Game> games = Map<String, Game>();
static StreamController<Map<String, Game>> _onUpdateController = StreamController.broadcast();
static Stream get onUpdate => _onUpdateController.stream;
static void setupListener() {
print('setupListener');
DocumentReference reference = Firestore.instance.collection('schedule').document('2018');
reference.snapshots().listen((documentSnapshot) {
print('listen begin');
List<Game> sortedList = List<Game>();
documentSnapshot.data.values.forEach((value) {
Game game = Game.fromMap(value);
sortedList.add(game);
});
sortedList.sort((a,b) => a.startTime.compareTo(b.startTime));
games.clear();
sortedList.forEach((game) {
games.addAll({game.key: game});
});
_onUpdateController.add(games);
print('listen end');
print('games: ${games.length}');
});
}
}
Related
I am trying to create a fetchUserOrders() method in an OrderCubit. To do so, I need the userId which is held in the OrderState of the OrderCubit.
Both Cubits are setup in a MultiBlocProvider in main.dart.
How do I make a call like:
Future<void> fetchOrders() async {
emit(OrderState.loading());
final uid = context.read<AuthCubit>().state.uid;
final orders = await OrderRepo().getUserOrders(userId: uid);
emit(OrderState.loaded(orders));
}
within the OrderCubit?
I don't know of a way to get a context within a Cubit, so the above is not an option thus far. I have looked at using BlocListener, but as I understand it that only emits a new state in response to a change from AuthCubit - which is not refreshing a new value.
I appreciate any insight on best practice for reading values from one Cubit to another.
I'm new to flutter and I have experience in web application using state managements like Redux or Vuex where the initial state of a module might be something like:
{
value1: 0,
value2: 10,
aBool: false,
aString: 'Hello'
}
Then based on Reducers or Mutations we can update a single or multiple properties of the state.
Now, learning Flutter I decided to use Bloc/Cubit and online I cannot find the right answer to my problem, even because the majority of the example are always based on the crappy counter app and never on a more realistic scenario.
All I can see is something based on 4 states in Bloc: initial, loading, success and error.
This is fine when fetching data from an API, but what if my state has also more properties?
how to update those properties?
Actually I created my test Cubit to fetch something from my API, it works. Now I wish to add more properties on the state and update it based on actions, how can I do that?
Example state:
#freezed
abstract class TestState with _$TestState {
const factory TestState.initial() = _Initial;
const factory TestState.loading() = _Loading;
const factory TestState.success(UserData user) = _Success;
const factory TestState.error(String message) = _Error;
}
Example Cubit:
class TestCubit extends Cubit<TestCubit> {
TestCubit(this._testClient)
: super(TestState.initial());
final TestClient _testClient;
String greet = 'Hi';
Future<void> testFetchData() async {
...
emit(TestState.success(testData));
...
}
}
I can successfully handle the varioud initial, loading, etc... states.
I can correctly watch at the greet property: context.read<TestCubit>().greet
How should I now update that value with 'hello!'?
// TestCubit
updateGreet(String text) {
emit(I don't know);
}
I omitted all my various tries to update that value.
Thanks
I am creating a reactive application with Meteor (with MongoDB as a backend).
I initially created a non-reactive-aware collection and denormalizers, eg.:
class DocCollection extends Mongo.Collection {
insert(doc, callback) {
const docId = super.insert(doc, callback);
doc = docMongo.findOne(docId); // for illustration, A
console.log(doc);
return docId;
}
}
docMongo = new DocCollection();
Now, I'd like to wrap it into MongoObservable, which will facilitate listening to the changes to the collection:
export const Doc = new MongoObservable.Collection(docMongo);
Then, I define a Method:
Meteor.methods({
add_me() {
Doc.insert(myDoc);
}
});
in server/main.js and call it in app.component.ts's constructor:
#Component(...)
export class AppComponent {
constructor() {
Meteor.call('add_me');
}
}
I get undefined printed to console (unless I sleep a little before findOne), so I suppose when I was looking for the doc after insertion in my Mongo.Collection, the document wasn't yet ready to be searched for.
Why does it happen, even though I overwrote the non-reactive class and only then wrapped it in MongoObservable?
How do I typically do denormalization with a reactive collection? Should I pass observables to my denormalizers and there create new ones, or is it possible to nicely wrap the non-reactive code afterwards (like I tried and failed above)? Note that I don't want to directly pass doc inside, as in more complex scenarios it will cause other inserts/updates elsewhere for which I'd also want to wait.
How do people typically test these things? If I run a test, the code above may succeed locally, as db insertion time is small, but fail when the delay is higher.
I am working on a server component which is responsible for caching models in memory and then stream any changes to interested clients.
When the first client requests a model (well model key, each model has a key to identify it) the model will be created (along with any subscriptions to downstream systems) and then sent to the client, followed by a stream of updates (generated by downstream systems). Any subsequent client's should get this cached (updated) model, again with the stream of updates. When the last client unsubscribes to the model the downstream subscriptions should be destroyed and the cached model destroyed.
Could anyone point me in the right direction as regards to how Rx could help here. I guess what isn't clear to me at the moment is how I synchronize state (of the object) and the stream of changes? Would I have two separate IObservables for the model and updates?
Update: here's what I have so far:
Model model = null;
return Observable.Create((IObserver<ModelUpdate> observer) =>
{
model = _modelFactory.GetModel(key);
_backendThing.Subscribe(model, observer.OnNext);
return Disposable.Create(() =>
{
_backendThing.Unsubscribe(model);
});
})
.Do((u) => model.MergeUpdate(u))
.Buffer(_bufferLength)
.Select(inp => new ModelEvent(inp))
.Publish()
.RefCount()
.StartWith(new ModelEvent(model)
If I understood the problem correctly, there are Models coming in dynamically. At any point in time in your Application's lifetime, the number of Models are unknown.
For that purpose an IObservable<IEnumerable<Model>> looks like a way to go. Each time there is a new Model added or an existing one removed, the updated IEnumerable<Model> would be streamed. Now it would essentially preserve the older objects as opposed to creating all Models each time there is an update unless there is a good reason to do so.
As for the update on each Model object's state such as any field value or property value changed, I would look into Paul Betts' ReactiveUI project, it has something called ReactiveObject. Reactive object helps you get change notifications easily, but that library is mainly designed for WPF MVVM applications.
Here is how a Model's state update would go with ReactiveObject
public class Model : ReactiveObject
{
int _currentPressure;
public int CurrentPressure
{
get { return _currentPressure; }
set { this.RaiseAndSetIfChagned(ref _currentPressure, value); }
}
}
now anywhere you have Model object in your application you could easily get an Observable that will give you updates about the object's pressure component. I can use When or WhenAny extension methods.
You could however not use ReactiveUI and have a simple IObservable whenever a state change occurs.
Something like this may work, though your requirements aren't exactly clear to me.
private static readonly ConcurrentDictionary<Key, IObservable<Model>> cache = new...
...
public IObservable<Model> GetModel(Key key)
{
return cache.GetOrAdd(key, CreateModelWithUpdates);
}
private IObservable<Model> CreateModelWithUpdates(Key key)
{
return Observable.Using(() => new Model(key), model => GetUpdates(model).StartWith(model))
.Publish((Model)null)
.RefCount()
.Where(model => model != null);
}
private IObservable<Model> GetUpdates(Model model) { ... }
...
public class Model : IDisposable
{
...
}
I have a singleton IObservable that returns the results of a Linq query. I have another class that listens to the IObservable to structure a message. That class is Exported through MEF, and I can import it and get asynchronous results from the Linq query.
My problem is that after initial composition takes place, I don't get any renotification on changes when the data supplied to the Linq query changes. I implemented INotifyPropertyChanged on the singleton, thinking it word make the exported class requery for a new IObservable, but this doesn't happen.
Maybe I'm not understanding something about the lifetime of MEF containers, or about property notification. I'd appreciate any help.
Below are the singleton and the exported class. I've left out some pieces of code that can be inferred, like the PropertyChanged event handlers and such. Suffice to say, that does work when the underlying Session data changes. The singleton raises a change event for UsersInCurrentSystem, but there is never any request for a new IObservable from the UsersInCurrentSystem property.
public class SingletonObserver: INotifyPropertyChanged
{
private static readonly SingletonObserver _instance = new SingletonObserver();
static SingletonObserver() { }
private SingletonObserver()
{
Session.ObserveProperty(xx => xx.CurrentSystem, true)
.Subscribe(x =>
{
this.RaisePropertyChanged(() => this.UsersInCurrentSystem);
});
}
public static SingletonObserverInstance { get { return _instance; } }
public IObservable<User> UsersInCurrentSystem
{
get
{
var x = from user in Session.CurrentSystem.Users
select user;
return x.ToObservable();
}
}
}
[Export]
public class UserStatus : INotifyPropertyChanged
{
private string _data = string.Empty;
public UserStatus
{
SingletonObserver.Instance.UsersInCurrentSystem.Subscribe(sender =>
{
//set _data according to information in sender
//raise PropertyChanged for Data
}
}
public string Data
{
get { return _data; } }
}
}
My problem is that after initial composition takes place, I don't get any renotification on changes when the data supplied to the Linq query changes.
By default MEF will only compose parts once. When a part has been composed, the same instance will be supplied to all imports. The part will not be recreated unless you explicitly do so.
In your case, if the data of a part change, even if it implements INotifyPropertyChanged, MEF will not create a new one, and you don't need to anyway.
I implemented INotifyPropertyChanged on the singleton, thinking it word make the exported class requery for a new IObservable
No.
Maybe I'm not understanding something about the lifetime of MEF containers, or about property notification.
Property notification allows you to react to a change in the property and has no direct effect on MEF. As for the container's lifetime, it will remain active until it is disposed. While it is still active, the container will keep references to it's compose parts. It's actually a little more complex than that, as parts can have different CreationPolicy that affects how MEF holds the part, I refer you to the following page: Parts Lifetime for more information.
MEF does allow for something called Recomposition. You can set it likewise:
[Import(AllowRecomposition=true)]
What this does tough is allow MEF to recompose parts when new parts are available or existing parts aren't available anymore. From what I understand it isn't what you are referring to in your question.