diff --git a/src/store.ts b/src/store.ts index abe10da3..37ebf32a 100644 --- a/src/store.ts +++ b/src/store.ts @@ -166,39 +166,46 @@ export function connect(mapStateToProps, options: any = {}) { } ); } - mounted() { - this.env.store.on("update", this, () => { - const ownProps = this.__owl__.ownProps; - const storeProps = mapStateToProps(this.env.store.state, ownProps); - const options: any = { - currentStoreProps: this.__owl__.currentStoreProps - }; - const storeHash = hashFunction( - { - state: this.env.store.state, - storeProps: storeProps, - revNumber, - deepRevNumber - }, - options - ); - let didChange = options.didChange; - if (storeHash !== this.__owl__.storeHash) { - didChange = true; - this.__owl__.storeHash = storeHash; - } - if (didChange) { - this.__owl__.currentStoreProps = storeProps; - this._updateProps(ownProps, false); - } - }); - super.mounted(); + /** + * We do not use the mounted hook here for a subtle reason: we want the + * updates to be called for the parents before the children. However, + * if we use the mounted hook, this will be done in the reverse order. + */ + _callMounted() { + this.env.store.on("update", this, this._checkUpdate); + super._callMounted(); } willUnmount() { this.env.store.off("update", this); super.willUnmount(); } - _updateProps(nextProps, forceUpdate) { + + _checkUpdate() { + const ownProps = this.__owl__.ownProps; + const storeProps = mapStateToProps(this.env.store.state, ownProps); + const options: any = { + currentStoreProps: this.__owl__.currentStoreProps + }; + const storeHash = hashFunction( + { + state: this.env.store.state, + storeProps: storeProps, + revNumber, + deepRevNumber + }, + options + ); + let didChange = options.didChange; + if (storeHash !== this.__owl__.storeHash) { + didChange = true; + this.__owl__.storeHash = storeHash; + } + if (didChange) { + this.__owl__.currentStoreProps = storeProps; + this._updateProps(ownProps, false); + } + } + _updateProps(nextProps, forceUpdate, p?: any) { if (this.__owl__.ownProps !== nextProps) { this.__owl__.currentStoreProps = mapStateToProps( this.env.store.state, @@ -211,7 +218,7 @@ export function connect(mapStateToProps, options: any = {}) { nextProps, this.__owl__.currentStoreProps ); - return super._updateProps(mergedProps, forceUpdate); + return super._updateProps(mergedProps, forceUpdate, p); } }; }; diff --git a/tests/store.test.ts b/tests/store.test.ts index 9f102ec5..27305b20 100644 --- a/tests/store.test.ts +++ b/tests/store.test.ts @@ -670,4 +670,48 @@ describe("connecting a component to store", () => { "
taster:matthieuconsumed:jupiler
" ); }); + + test("correct update order when parent/children are connected", async () => { + const steps: string[] = []; + class Parent extends Component { + inlineTemplate = ` +
+ +
+ `; + widgets = { Child: ConnectedChild }; + } + const ConnectedParent = connect(function(s) { + steps.push("parent"); + return { current: s.current, isvisible: s.isvisible }; + })(Parent); + + class Child extends Component { + inlineTemplate = ``; + } + + const ConnectedChild = connect(function(s, props) { + steps.push("child"); + return { msg: s.msg[props.key] }; + })(Child); + + const state = { current: "a", msg: { a: "a", b: "b" } }; + const mutations = { + setCurrent({ state }, c) { + state.current = c; + } + }; + + const store = new Store({ state, mutations }); + (env).store = store; + const app = new ConnectedParent(env); + + await app.mount(fixture); + expect(fixture.innerHTML).toBe("
a
"); + expect(steps).toEqual(["parent", "child"]); + + store.commit("setCurrent", "b"); + await nextTick(); + expect(steps).toEqual(["parent", "child", "parent", "child", "child"]); + }); });