Mobx.dart: ObservableMap doesn't trigger a refresh

Created on 13 Sep 2019  路  17Comments  路  Source: mobxjs/mobx.dart

Not sure if it's a bug or if it's just not implemented yet (as it's not checked on the Readme)

But here I am :)

I'm using this:

@observable
ObservableMap<String, dynamic> currentCustomData = ObservableMap.of({});
....
//Then
currentCustomData['data'] = data;

But look like my Observer is not triggered so I'm not getting the fresh data, is there any way for me to manually trigger an update of the map ? (until you fix or implement this)

Thanks!

All 17 comments

This should be working. Can you show me your Observer code ?

Sure:

class AddDeviceListStep extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final store = Provider.of<AddDeviceStore>(context);
    final translations = CommonLocalizations.of(context);

    return Observer(
      builder: (context) {
        final items = store.availableDevices;
        final step = store.currentCustomStep['step'];
        final singleChoice = store.currentCustomStep['singleChoice'] ?? false;
        final selectedItems = store.currentCustomData[step]; // List of items or selected item if singleChoice

        if (items == null || items.length == 0) {
          return Center(child: Text(translations.emptyList));
        }

        return Column(
          children: <Widget>[
            .....
          ],
        );
      },
    );
  }
}

I tried this and it seems to be working. I wrote a test as well to verify: https://github.com/mobxjs/mobx.dart/blob/master/mobx/test/observable_map_test.dart#L133

The test shows that keys can be added and reactions that use it will trigger as expected

Is it still failing @jaumard ? Was the test relevant for you? Note that an Observer is using a reaction internally, which is why I made a test with it.

Yes it's still failing for me, let me try to do a small reproducible sample :) I might do something wrong then, for now I've patch by simply modifying another field that is just a boolean to trigger an update on observer

A repro would be great @jaumard

I'm not able to reproduce on a small example and I'm not able to find where my problem was lool

But I'm guessing it was something like this:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart' as prefix0;
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';

part 'main.g.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final store = MyStore();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      floatingActionButton: FloatingActionButton(onPressed: () {
        store.setData('test');
      }),
      body: Observer(
        builder: (BuildContext context) {
          return prefix0.HookBuilder(
            builder: (BuildContext context) {
              return Text(store.data['data'] ?? 'not SET');
            },
          );
        },
      ),
    );
  }
}

class MyStore = _MyStore with _$MyStore;

abstract class _MyStore with Store {
  @observable
  ObservableMap<String, String> data = ObservableMap();

  @action
  void setData(String data) {
    this.data['data'] = data;
  }
}

Here in this case I can see There are no observables detected in the builder function for Observer but if I use another field inside the Observer the message is gone and my Observer will not be updated for the map (I guess that was my case...)

If for some reason I can't switch the order of the builder and they have to stay how they are. Is mobX offering a solution to still be able to update my widgets ?

From the looks of it, it seems like your Observer should really be wrapping the Text widget instead

But I was wondering if mobX offer a way to detect such problem, meaning using a field outside of an Observer. Because now I know it I'll try to be careful but for new people or even everybody if they are not paying attention they will have a bug that can be hard to find

Ya that's a good point. We are throwing warnings on the console if an Observer does not find any observables, but I guess we could do it visually as well by showing an error-box on the Observer widget ?

Curious to hear the opinions of @rrousselGit @shyndman

Hey @jaumard,

Were you by any chance writing to the ObservableMap inside the Observer's builder? We actually just ran into a bug with Observer where writes to observed properties from inside the builder silently breaks the observation. I'll report it separately.

@pavanpodila, maybe? Couldn't hurt.

We should find a way to report mutations inside the builder function. Certainly not a good practice

Ya that's a good point. We are throwing warnings on the console if an Observer does not find any observables, but I guess we could do it visually as well by showing an error-box on the Observer widget ?

Throwing inside the build method of Observer instead of printing a message should be enough

@jaumard 's point is different. MobX should print some warning or info message also if some observable is outside Observer.builder. It will help if someone forgets to wrap the observable inside observer.

@jaumard 's point is different. MobX should print some warning or info message also if some observable is outside Observer.builder. It will help if someone forgets to wrap the observable inside observer.

We can change the message in the error to include this part: "You may have referenced an observable outside the builder function"

Even if we have observables inside all Observers, we may still have some observable outside the Observers which was supposed to be wrapped inside another Observer. In that case another kind of message can be printed: "You have observable(s) outside Observer(s)."

Probably better to list all possible situations:

  1. You may have referenced an observable outside the builder function
  2. Some of your observables were not referenced inside the Observer
Was this page helpful?
0 / 5 - 0 ratings