[FIX] context: always remove subscription when destroyed

Before this rev., components destroyed before being mounted didn't
stop listening to the context changes.

Closes #476
This commit is contained in:
Aaron Bohy
2019-11-20 11:09:19 +01:00
committed by Géry Debongnie
parent 2922cee6ea
commit 28ee790b3e
2 changed files with 37 additions and 3 deletions
+5 -3
View File
@@ -2,7 +2,7 @@ import { Component } from "./component/component";
import { scheduler } from "./component/scheduler";
import { EventBus } from "./core/event_bus";
import { Observer } from "./core/observer";
import { onWillUnmount } from "./hooks";
/**
* The `Context` object provides a way to share data between an arbitrary number
* of component. Usually, data is passed from a parent to its children component,
@@ -138,9 +138,11 @@ export function useContextWithCB(ctx: Context, component: Component<any, any>, m
await method();
}
});
onWillUnmount(() => {
const __destroy = component.__destroy;
component.__destroy = (parent) => {
ctx.off("update", component);
delete mapping[id];
});
__destroy.call(component, parent);
}
return ctx.state;
}
+32
View File
@@ -255,6 +255,38 @@ describe("Context", () => {
expect(testContext.subscriptions.update.length).toBe(0);
});
test("destroyed component before being mounted is inactive", async () => {
const testContext = new Context({ a: 123 });
const def = makeDeferred();
class Child extends Component<any, any> {
static template = xml`<span><t t-esc="contextObj.a"/></span>`;
contextObj = useContext(testContext);
willStart() {
return def;
}
}
class Parent extends Component<any, any> {
static template = xml`<div><Child t-if="state.flag"/></div>`;
static components = { Child };
state = useState({ flag: true });
}
const parent = new Parent();
const prom = parent.mount(fixture);
await nextTick(); // wait for Child to be instantiated
expect(testContext.subscriptions.update.length).toBe(1);
parent.state.flag = false;
await prom;
expect(fixture.innerHTML).toBe("<div></div>");
def.resolve(); // must wait for willStart promise to be resolved
await nextTick();
// kind of whitebox...
// we make sure we do not have any pending subscriptions to the 'update'
// event
expect(testContext.subscriptions.update.length).toBe(0);
});
test("concurrent renderings", async () => {
const testContext = new Context({ x: { n: 1 }, key: "x" });
const def = makeDeferred();