Yomguithereal/baobab-react

Caching child components

tnrich opened this issue · 5 comments

Hey @Yomguithereal, I am interested in learning more about how baobab-react triggers a rerender of a react component.

Say I have a component that renders a bunch of children components and "caches" some of them in an attempt to make rendering faster.

var ParentComponent = React.createClass({
    componentWillReceiveProps: function(nextProps) {
        if (this.props.childrenData !== nextProps.childrenData) {
            this.cachedChildComponents = {};
        }
    },
    render: function () {
        var self = this;
        return this.props.childrenData.map(function(data){
            var childComp;
            if (self.cachedChildComponents[data.id]) {
                childComp = self.cachedChildComponents[data.id]
            } else {
                childComp = (<ChildComponent
                    key={data.id}
                    data={data}
                />);
                self.cachedChildComponents[data.id] = childComp;
            }
            return (
                <div>
                    {childComp}
                </div>
            )
        });
    }
})

but the child component is listening to an outside baobab data source

var ChildComponent = React.createClass({
    mixins: [baobabBranch],
    cursors: {
        outsideData: ['outsideData'],
    },
    render: function() {
        return (
            <div>
                {this.props.data} 
                {this.state.outsideData}
            </div>
        )
    }
})

The issue I'm having is that when the outsideData source is changed, my children components aren't updating themselves. They display normally initially, but then changing outsideData does nothing. If I don't try to "cache" them, they do update themselves properly, but they are slow in doing it.

Note, while writing this question, I realized I could apply a pure-render mixin to a lot of the subcomponents contained within my ChildComponent so that they render significantly faster so I don't think I'll need to "cache" them after all, but I am still interested in trying to understand why the caching I was doing was preventing the child components from updating themselves.

Have you ever seen something like this?

Thanks for all your help!
Thomas

PS
If you want to see where I initially ran into this issue, you can check out the little app I'm building if you feel like it:
https://github.com/tnrich/webpackTrial/tree/infinte-scroll-lazy-loading
You'll see that the editor doesn't respond to click/drag events like normal (because of the caching of the rowItems that is happening in the InfiniteScroller component).

Well this is certainly a tough one @tnrich. I think you are getting this problem because baobab-react bind listeners to the tree on componentWillMount. So, because the cached children may not be mounted, listeners are not bound and do not re-render with the tree. Would this explain your problem?

Hey @Yomguithereal, that does indeed seem like it could be the source of the problem. What is confusing though, is that because the children are rendering, it seems like they would have to have had to have called componentWillMount at some point, right? Is it that they being unmounted at a later point, and then not remounted?

Do you have any suggestions for getting around this issue if I still wanted to try to cache the child components?

Thanks so much!

Because the components render but are not mounted on the DOM, React must not trigger the componentWillMount hook.

Are you using mixins or HOC here? I guess it could be possible somehow to call the componentWillMount function manually. Or find another hook which would better work in our case (I am not sure a better hook exists).

Do you have any example of components performing the kind of caching you are doing here @tnrich?

Here's where I would do it:
https://github.com/tnrich/webpackTrial/blob/infinte-scroll-lazy-loading/app/InfiniteScroller.js

var rowItems = this.state.visibleRows.map(function(row) {
      // return self.props.renderFunction(row);
      var newItem;
      if (!self.items) {
        self.items = {};
      }
      if (self.items[row.rowNumber]) {
        newItem = self.items[row.rowNumber];
      } else {
        newItem = self.props.renderFunction(row);
        self.items[row.rowNumber] = newItem;
      }
      return newItem;
    });