preact-compat icon indicating copy to clipboard operation
preact-compat copied to clipboard

parent's componentDidUpdate trigger before child's componentDidMount

Open gogoyqj opened this issue 8 years ago • 9 comments

jsx like below:

<scroller>
    <Lazyimage />
</scroller>

preact if 'rerender' called

[email protected]:3876     Scroller     componentDidMount
[email protected]:3920     Scroller     componentDidUpdate
[email protected]:5633     Lazyimage  componentDidMount

In react

[email protected]:3876     Scroller     componentDidMount
[email protected]:5633     Lazyimage  componentDidMount
[email protected]:3920     Scroller     componentDidUpdate

the difference in trigger order may cause problem

gogoyqj avatar Mar 01 '17 10:03 gogoyqj

Ah yeah that makes sense, since Preact defers componentDidMount until the very end of the cycle.

developit avatar Mar 01 '17 23:03 developit

seems that business logic should avoid depending on unpredictable trigger order is the best practice.

I'll change my scroller & Lazyimage

gogoyqj avatar Mar 02 '17 02:03 gogoyqj

That'd be ideal yeah - mind cc'ing me on your change or sending me a link? I like to keep on top of these things 👍

developit avatar Mar 02 '17 19:03 developit

class Scroller extends Component {

    constructor() {
        this.lazyImgs = [];
    }

    componentDidMount() {
        this._loadImg();
    }

    componentDidUpdate() {
        this._loadImg();
    }

    _loadImg() {
        if (this.lazyImgs.length) {
            this.lazyImgs.map(img => img.load());
        }
    }

}


class Lazyimage extends Component {

    componentDidMount() {
        this.scroller = this.props.scroller;
        if (this.scroller) {
            this.scroller.lazyImgs.push(this)
        }
        // here my change
        if (InView(this)) this.load()
    }

    load() {
        // load self
    }
}

I don't understand why they make the Scroller & Lazyimage work strictly depends on trigger order of parent's componentDidUpdate & children's componentDidMount.

In order to make Scroller & Lazyimage work also in preact, Lazyimage should make a decision whether to load itself when its componentDidMount trigger.

@developit that's all.

gogoyqj avatar Mar 03 '17 02:03 gogoyqj

@developit so that https://github.com/developit/preact/pull/506 is supposed to fix this?

NekR avatar Mar 04 '17 14:03 NekR

Just to add, I found a problem caused by this today. Was using react-bootstrap's modal and apparently it renders a component that creates an element on its componentDidUpdate. But since the order of the triggered lifecycle is different, everything just broke.

So yeah, would be nice to get this fixed :smile:

Vija02 avatar Mar 05 '17 01:03 Vija02

Ah interesting, that might make this the root cause of maybe 4 different issues.

developit avatar Mar 05 '17 03:03 developit

i wonder if changes thoses functions to ensure lifecycle in right order.

is there any side effect?

is there any thing that i neglect?

@developit

// in vdom/diff.js
export function flushMounts() {
    let c;
    while ((c=mounts.pop())) {
        // if (options.afterMount) options.afterMount(c);
        // if (c.componentDidMount) c.componentDidMount();
        if (typeof c === 'function') c();
    }
}
    // in vdom/component.js
    if (!isUpdate || mountAll) {
        // mounts.unshift(component);
        if (options.afterMount) mounts.unshift(() => options.afterMount(component));
        if (component.componentDidMount) mounts.unshift(component.componentDidMount.bind(component));
    }
    else if (!skip) {
        // if (component.componentDidUpdate) {
        //  component.componentDidUpdate(previousProps, previousState, previousContext);
        // }
        // if (options.afterUpdate) options.afterUpdate(component);
        if (options.afterUpdate) mounts.unshift(() => options.afterUpdate(component));
        if (component.componentDidUpdate) mounts.unshift(component.componentDidUpdate.bind(component, previousProps, previousState, previousContext));
    }

gogoyqj avatar Mar 28 '17 09:03 gogoyqj

@gogoyqj that would create a closure on every call to renderComponent(), which is very very often. Generally we try to avoid ever creating closures (I don't think there are any currently in the codebase).

developit avatar Mar 28 '17 19:03 developit