[FIX] store: ensure useStore proper clean up on destroy

This commit is contained in:
Sébastien Theys
2019-11-27 14:10:47 +01:00
committed by Géry Debongnie
parent 6c753bb49f
commit a6f9b26057
2 changed files with 31 additions and 6 deletions
+12 -6
View File
@@ -84,6 +84,7 @@ const isStrictEqual = (a, b) => a === b;
export function useStore(selector, options: SelectorOptions = {}): any {
const component: Component<any, any> = Component.current!;
const componentId = component.__owl__.id;
const store = options.store || (component.env.store as Store);
if (!(store instanceof Store)) {
throw new Error(`No store found when connecting '${component.constructor.name}'`);
@@ -92,11 +93,10 @@ export function useStore(selector, options: SelectorOptions = {}): any {
const hashFn = store.observer.revNumber.bind(store.observer);
let revNumber = hashFn(result);
const isEqual = options.isEqual || isStrictEqual;
if (!store.updateFunctions[component.__owl__.id]) {
store.updateFunctions[component.__owl__.id] = [];
if (!store.updateFunctions[componentId]) {
store.updateFunctions[componentId] = [];
}
const updateFunctions = store.updateFunctions[component.__owl__.id];
updateFunctions.push(function(): boolean {
store.updateFunctions[componentId].push(function(): boolean {
const oldResult = result;
result = selector(store!.state, component.props);
const newRevNumber = hashFn(result);
@@ -115,7 +115,7 @@ export function useStore(selector, options: SelectorOptions = {}): any {
useContextWithCB(store, component, function(): Promise<void> | void {
let shouldRender = false;
for (let fn of updateFunctions) {
for (let fn of store.updateFunctions[componentId]) {
shouldRender = fn() || shouldRender;
}
if (shouldRender) {
@@ -123,9 +123,15 @@ export function useStore(selector, options: SelectorOptions = {}): any {
}
});
onWillUpdateProps(props => {
delete store.updateFunctions[component.__owl__.id];
result = selector(store.state, props);
});
const __destroy = component.__destroy;
component.__destroy = parent => {
delete store.updateFunctions[componentId];
__destroy.call(component, parent);
};
if (typeof result !== "object") {
return result;
}
+19
View File
@@ -490,6 +490,25 @@ describe("connecting a component to store", () => {
expect(fixture.innerHTML).toBe("<div><span>jupiler</span><span>kwak</span></div>");
});
test("connected component is properly cleaned up on destroy", async () => {
class App extends Component<any, any> {
static template = xml`<div></div>`;
state = useStore((state, props) => state);
}
const store = new Store({ state: {} });
(<any>env).store = store;
const app = new App();
await app.mount(fixture);
expect(fixture.innerHTML).toBe("<div></div>");
expect(store.updateFunctions[app.__owl__.id].length).toBe(1);
app.destroy();
expect(store.updateFunctions[app.__owl__.id]).toBe(undefined);
});
test("connected component with undefined, null and string props", async () => {
class Beer extends Component<any, any> {
static template = xml`