diff --git a/src/context.ts b/src/context.ts index 84848515..1ef049f0 100644 --- a/src/context.ts +++ b/src/context.ts @@ -81,13 +81,7 @@ export class Context extends EventBus { const subscriptions = this.subscriptions.update; const groups = partitionBy(subscriptions, s => (s.owner ? s.owner.__owl__.depth : -1)); for (let group of groups) { - const proms = Promise.all( - group.map(sub => { - if (sub.owner ? sub.owner.__owl__.isMounted : true) { - return sub.callback.call(sub.owner, rev); - } - }) - ); + const proms = group.map(sub => sub.callback.call(sub.owner, rev)); // at this point, each component in the current group has registered a // top level fiber in the scheduler. It could happen that rendering these // components is done (if they have no children). This is why we manually @@ -96,7 +90,7 @@ export class Context extends EventBus { // promise to resolve earlier, which means that there is a chance of // processing the next group in the same frame. scheduler.flush(); - await proms; + await Promise.all(proms); } } } diff --git a/src/store.ts b/src/store.ts index ccb2a1e7..12478aa5 100644 --- a/src/store.ts +++ b/src/store.ts @@ -111,9 +111,9 @@ export function useStore(selector, options: SelectorOptions = {}): any { useContextWithCB(store, component, function(): Promise | void { let shouldRender = false; - updateFunctions.forEach(function(updateFn) { - shouldRender = updateFn() || shouldRender; - }); + for (let fn of updateFunctions) { + shouldRender = fn() || shouldRender; + } if (shouldRender) { return component.render(); } diff --git a/tests/store_hooks.test.ts b/tests/store_hooks.test.ts index e2fc836c..75dea74a 100644 --- a/tests/store_hooks.test.ts +++ b/tests/store_hooks.test.ts @@ -572,6 +572,31 @@ describe("connecting a component to store", () => { ); }); + test("store changes occuring when mounting a component are notified", async () => { + const initialState = { x: { val: 1 } }; + const actions = { + setValue({ state }, val) { + state.x.val = val; + } + }; + class Parent extends Component { + static template = xml`
`; + x = useStore(state => { + return Object.assign({}, state.x); + }); + dispatch = useDispatch(); + } + Parent.prototype.__render = jest.fn(Parent.prototype.__render); + Parent.env.store = new Store({ state: initialState, actions }); + + const parent = new Parent(); + const prom = parent.mount(fixture); + parent.dispatch("setValue", 2); + await prom; + expect(fixture.innerHTML).toBe("
2
"); + expect(Parent.prototype.__render).toHaveBeenCalledTimes(1); + }); + test("correct update order when parent/children are connected", async () => { const steps: string[] = []; @@ -899,6 +924,46 @@ describe("connecting a component to store", () => { expect(steps).toEqual(["on:update", "off:update"]); }); + test("connected child component destroyed by dispatched action", async () => { + let steps: any = []; + + class Child extends Component { + static template = xml`
`; + store = useStore(s => { + steps.push("child selector"); + return s; + }); + } + class Parent extends Component { + static template = xml`
`; + static components = { Child }; + store = useStore(s => { + steps.push("parent selector"); + return s; + }); + dispatch = useDispatch(); + } + + const state = { child: true, val: 1 }; + const actions = { + toggleChild({ state }) { + state.child = !state.child; + } + }; + const store = new Store({ state, actions }); + (env).store = store; + const parent = new Parent(); + + await parent.mount(fixture); + expect(fixture.innerHTML).toBe("
1
"); + expect(steps).toEqual(["parent selector", "child selector"]); + + parent.dispatch("toggleChild"); + await nextTick(); + expect(fixture.innerHTML).toBe("
"); + expect(steps).toEqual(["parent selector", "child selector", "parent selector"]); + }); + test("dispatch an action", async () => { class App extends Component { static template = xml`
`;