Confused about how data propagates with DDAU

I’m experiencing some unexpected behaviour when trying to implement DDAU when setting properties that are not contained in objects.

I’ve created an Ember Twiddle showing the behaviour here. The code is below:

controllers/application.js

The controller is the owner of the data, which is comprised of foo (An object) and baz, a string. An action called setProperty is included, to be called when those data properties need to be updated. My understanding of DDAU is that this is done because data should only be mutated at the level which owns it.

  export default Ember.Controller.extend({
  init() {
    this._super(...arguments);
    this.foo = {
      bar: 'one'
    };
    this.baz = 'two'
  },
  actions: {
    setProperty(prop, value) {
     	this.set(prop, value); 
    }
  }
});

templates/application.hbs

Simply inserts a component, passing in the data props and the closure action.

{{my-component
  foo=foo
  baz=baz
  setProperty=(action "setProperty")
}}

components/my-component.js

export default Component.extend({
  actions: {
    updateProps() {
      this.setProperty('foo.bar', 'one-updated');
      console.log(this.get('foo.bar')); // one-updated
      this.setProperty('baz', 'two-updated');
      console.log(this.get('baz')); // two
      setTimeout(() => {
         console.log(`setTimeout: ${this.get('baz')}`); // two-updated
      });
    }
  }
});

templates/components/my-component.hbs

baz value: {{baz}}<br>
foo.bar value: {{foo.bar}}<br>
<button {{action "updateProps"}}>Update Props</button>

When the Update Props button in the component is clicked, the setProperty action is fired and the properties are updated as defined in the action. Both foo.bar and baz updated in the component template as expected.

Here is where things get weird.

Immediately after each time that I call the setProperty action from the component, I log the relevant property to the console. In the case of foo.bar, the console shows the updated value as expected.

However, logging baz to the console after calling setProperty logs the initial value, not the updated one. Even weirder is that if I wrap the console.log statement in a setTimeout, it logs the updated value, even if no time interval is passed as an argument.

I have three questions.

  1. Why has baz not been updated by the time it is logged to the console and is there a way around this?
  2. Why would the setTimeout make a difference?
  3. Am I misunderstanding the best way to implement DDAU? Do I need to fundamentally reconsider my design pattern?

You haven’t explained why synchronously accessing this.baz inside updateProps and seeing the new value is important to you. It probably shouldn’t be. If you’re trying to do some work in that spot to manually propagate the change, that is the problem.

Another way to say this is: the component can’t safely assume that all changes to foo or bar will come from itself. So whatever you’re trying to do in reaction to them changing needs to be done in a place other than your own updateProps anyway.

The explanation for the difference between foo and bar is that both are getting passed into your component only when the component gets rendered or re-rendered. Both of them will only update after a render happens. But setProperty isn’t changing foo, it’s changing a property inside foo, so you necessary see that change immediately, because you’re sharing one copy of the object between the controller and the component.

1 Like

Thank you, this makes a lot of sense. It helps to know why that pattern is unreliable, and that it should simply be avoided.