Yomguithereal / baobab

JavaScript & TypeScript persistent and optionally immutable data tree with cursors.
MIT License
3.15k stars 115 forks source link

Very slight delay in updated state getting to cursors #346

Open williamrenwick opened 9 years ago

williamrenwick commented 9 years ago

I'm currently experiencing an issue with my website where whenever I update the state inside componentDidMound and then want to access the updated state within the same component I have to implement a setTimeout to allow for the state to update - it's usually only a period of 20ms but it's still inelegant and I'd like to know if it's just something I'm doing wrong?

Let me give you an example, I have a set of div elements that I need to get the positions of from the browser once they've mounted, which I'm doing like so:

homepageListItems.js

var homepageListItem = React.createClass({
    mixins: [mixin],
cursors: {
    posts: ['posts']
},
componentDidMount: function() {
    var liPosition = this.getPosition();

    hpPostActions.updatePosts(liPosition);

    console.log(this.state.posts)
},
getPosition: function() {
    var node = React.findDOMNode(this.refs.hpWorkItem);
    var height = node.clientHeight;
    var topPos = node.offsetTop;
    var bottomPos = topPos + height;

    return {
        id: this.props.project.index,
        height: height,
        topPos: topPos,
        bottomPos: bottomPos
    }
},
})

hpPostActions then pushes the returned objects into an array inside my state tree:

hpPostActions.js

var StateTree = require('../data/stateTree.js');
var cursor = StateTree.select('posts');

var postActions = {
    updatePosts: function(liPositions) {
        cursor.push(liPositions);
        StateTree.commit();
    }
 }

 module.exports = postActions;

stateTree.js

var Baobab = require('baobab');

var stateTree = new Baobab({
    posts: []  
})

module.exports = stateTree;

However when it consoles this.state.posts inside componentDidMount it just returns empty arrays unless wrapped in a timeout. Just FYI this has happened elsewhere in the application when I need to update the scroll position inside of my state tree when a particular component has mounted. Any pointers in how to do this better?

tnrich commented 9 years ago

Hey there,

@Yomguithereal, correct me if I'm wrong, but I believe the delay you're seeing is a consequence of baobab's default behavior of batching multiple updates together and then asynchronously committing them and emitting an 'updated' event (which is what your react component would be listening to).

I think you should be able to call tree.commit() to force the tree to update when you want it to. I would give that a try and see if it works.

Here's the documentation under the 'Updates' section which describes it more precisely:

Updates

A baobab tree can obviously be updated. However, one has to understand that the library won't do so, at least by default, synchronously.

Rather, the tree will stack and merge every update order you give it and will only commit them later on (note that you remain free to force a synchronous update of the tree through tree.commit() or by tweaking the tree's options).

This enables the tree to perform efficient mutations and to be able to notify any relevant cursors that the data they are watching over has changed.

Yomguithereal commented 9 years ago

Hello @williamrenwick. @tnrich is quite right. In Boabab v1 (behavior has now changed in v2 and you should probably check it out because it might get simpler for your use case), writes are batched and solved asynchronously. You can circumvent this by doing either one of the following solutions:

williamrenwick commented 9 years ago

@Yomguithereal @tnrich Thanks for the response, however as you can see from the code supplied above I've already included the commit() method on my tree inside hpPostActions and I'm still experiencing a slight delay. Is there any other reason this might be, or is it just the run time taken for the action to set the state and for the cursors to be notified of said state change?

tnrich commented 9 years ago

Whoops, sorry @williamrenwick, I didn't see the call to commit() you had made. I guess there might be inherent asynchronicity between the update of the baobab tree, and the propagation of that update to the actual internal state of react components. Not sure why that would be exactly or if it is necessary to the system. I'm sure @Yomguithereal would know better than me.

Yomguithereal commented 9 years ago

I should read the whole thing once more. Let me check that.

Yomguithereal commented 9 years ago

Ok. @williamrenwick, components are bound to the tree's update events so this.state.posts won't update in the scope of your componentDidMount hook even if you commit changes synchronously. The 20ms thing is strange however. Is you app huge? Is your posts array containing a lot of elements?

Yomguithereal commented 9 years ago

What you could do if you wanted to access the updated data straight away would be to use the cursors created by the mixin (this.cursors.posts.get()) within the componentDidMount hook as a workaround.

Yomguithereal commented 8 years ago

Any update here?