Mobx: `intercept()` and `observe()` don't track nested observables

Created on 3 Jun 2016  ·  8Comments  ·  Source: mobxjs/mobx

var appState = observable({
    counter: 2,
    todos: [{
        title: 'Submit mobx issue',
        done: false
    }]
})
intercept(appState, function(change) {
    console.log(change)
    return change;
})
observe(appState, function(change) {
    console.log(change)
    return change;
})
appState.todos[0].title = "Get stuff done";

No output. Bug or feature?

❔ question 📖 documentation

Most helpful comment

intercept and observe are low level api's you should hardly need. Is it a problem that cannot be solved by e.g. reaction @matthewmueller? observe has many drawbacks like for example not respecting transactions.

The reason it doesn't deep observe is that it it cannot be generalized, if you have a cyclic structure deep observing will either utterly crash or be very expensive to prevent that. MobX doesn't make assumptions on your data structures and hence cannot generalize this. In contrast to for example mobx-state-tree, which does support deep observing out of the box, but as a tradeoff you are required to limit yourself to using data trees instead of data graphs.

All 8 comments

Intended unfortunately. Although I would like a way to be able to deeply observe as well. Currently there is spy that would work, but that is for all observables instead of a nested hierarchy.

So, autorun tracks only what properties that has been accessed (_just like observer I guess_)

Make sense but again I was a bit surprised. I will try to do a pull request for the docs and add a simple clarifying sentence.

Ah completely messed up previous comment. I meant to add that I got confused with autorun as well.

As for intercept() and observe() I will also include some clarification and the docs and make a pr.

I also wish there was a nested option for () and observe(). Otherwise this forces me to break down my state to stores on every nested object/array.

Doc PR's are more then welcome!

If you need to deep observe, I recommend using autorun or reaction. Did that fit? It is also good to note that you can make observe handlers generic; e.g. reuse the same callback over and over again because the causing object is always in change.object. (You still need to register them all though, so that is why I usually opt for autorun / reaction

so why this is intentional? is there a limitation in intercepting nested values? you can also listen on:

appState.todos.intercept(fn)

but maybe there's harm in handling both?

edit: nevermind on the intercept example above, that only works on things like push(...)

@matthewmueller

From what I know, intercept basically works like this, and thus can't look any deeper into your object:

spy(function(change) {
  // references match
  if (change.object === myObject) {
    // Do stuff with change info
  }
})

I experimented a lot with deep observation, and short of adding a non-enumerable field marking the parent of a given observable (which is in my opinion too much work), it didn't work well.


A possible alternative is wrapping all the changes you want to intercept inside of action, and then intercepting the action like so

const myObject = { a: 1 }

const modifyObject = action('modify', function() {
  myObject.a = 5
})

spy(function(change) {
  // action which modifies all your objects detected!
  if(change.type === 'action' && change.name === 'modify') {
    console.log(myObject) // myObject before it's changed!
  }
})

intercept and observe are low level api's you should hardly need. Is it a problem that cannot be solved by e.g. reaction @matthewmueller? observe has many drawbacks like for example not respecting transactions.

The reason it doesn't deep observe is that it it cannot be generalized, if you have a cyclic structure deep observing will either utterly crash or be very expensive to prevent that. MobX doesn't make assumptions on your data structures and hence cannot generalize this. In contrast to for example mobx-state-tree, which does support deep observing out of the box, but as a tradeoff you are required to limit yourself to using data trees instead of data graphs.

ah okay, thanks for the info & links guys. I think I'm definitely leaning more towards what you're doing with mobx-state-tree, basically trying to combine tcomb with mobx, using intercept to reject invalid state updates.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sonaye picture sonaye  ·  70Comments

mweststrate picture mweststrate  ·  105Comments

mweststrate picture mweststrate  ·  75Comments

bySabi picture bySabi  ·  95Comments

creativefctr picture creativefctr  ·  35Comments