diff --git a/examples/todoapp/templates.xml b/examples/todoapp/templates.xml
index a2290c86..d9daafc9 100644
--- a/examples/todoapp/templates.xml
+++ b/examples/todoapp/templates.xml
@@ -50,7 +50,7 @@
-
+
diff --git a/src/component.ts b/src/component.ts
index 82603c82..4636835a 100644
--- a/src/component.ts
+++ b/src/component.ts
@@ -319,10 +319,6 @@ export class Component<
nextProps: Props,
forceUpdate: boolean = false
): Promise {
- if (nextProps === this.__owl__.renderProps && !forceUpdate) {
- await this.__owl__.renderPromise;
- return;
- }
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve();
}
diff --git a/src/store.ts b/src/store.ts
index 590fc495..4bcb6545 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -1,4 +1,5 @@
import { EventBus } from "./event_bus";
+import { shallowEqual } from "./utils";
import { Component } from "./component";
//------------------------------------------------------------------------------
@@ -235,17 +236,23 @@ export function makeObserver(): Observer {
// Connect function
//------------------------------------------------------------------------------
-function setStoreProps(__owl__: any, storeProps: any) {
- __owl__.currentStoreProps = storeProps;
- __owl__.currentStoreRevs = {};
- __owl__.currentStoreRev = storeProps.__owl__ && storeProps.__owl__.rev;
- for (let key in storeProps) {
- __owl__.currentStoreRevs[key] =
- storeProps[key].__owl__ && storeProps[key].__owl__.rev;
+function revNumber(o: T): number {
+ if (!("__owl__" in o)) {
+ return 0;
}
+ return (o).__owl__.rev;
}
-export function connect(mapStateToProps) {
+function deepRevNumber(o: T): number {
+ if (!("__owl__" in o)) {
+ return 0;
+ }
+ return (o).__owl__.deepRev;
+}
+
+export function connect(mapStateToProps, options: any = {}) {
+ let hashFunction = options.hashFunction || null;
+
return function(Comp) {
return class extends Comp {
constructor(parent, props?: any) {
@@ -254,32 +261,60 @@ export function connect(mapStateToProps) {
const storeProps = mapStateToProps(env.store.state, ownProps);
const mergedProps = Object.assign({}, props || {}, storeProps);
super(parent, mergedProps);
- setStoreProps(this.__owl__, storeProps);
this.__owl__.ownProps = ownProps;
+ this.__owl__.currentStoreProps = storeProps;
+ if (!hashFunction) {
+ if ("__owl__" in storeProps) {
+ hashFunction = s => deepRevNumber(s.storeProps);
+ } else {
+ let areKeyObservable = false;
+ for (let key in storeProps) {
+ areKeyObservable =
+ areKeyObservable || "__owl__" in storeProps[key];
+ }
+ if (areKeyObservable) {
+ hashFunction = function({ storeProps }) {
+ return Object.values(storeProps).reduce(
+ (sum: number, val: any) => sum + deepRevNumber(val),
+ 0
+ );
+ };
+ }
+ }
+ }
+ if (hashFunction) {
+ this.__owl__.storeHash = hashFunction({
+ state: env.store.state,
+ storeProps: storeProps,
+ revNumber,
+ deepRevNumber
+ });
+ }
}
mounted() {
this.env.store.on("update", this, () => {
const ownProps = this.__owl__.ownProps;
const storeProps = mapStateToProps(this.env.store.state, ownProps);
let didChange = false;
- if (
- this.__owl__.currentStoreRev &&
- this.__owl__.currentStoreRev !== storeProps.__owl__.rev
- ) {
- setStoreProps(this.__owl__, storeProps);
- didChange = true;
- } else {
- const revs = this.__owl__.currentStoreRevs;
- for (let key in storeProps) {
- const val = storeProps[key];
- if (val.__owl__ && val.__owl__.rev !== revs[key]) {
- didChange = true;
- revs[key] = val.__owl__ && val.__owl__.rev;
- this.__owl__.currentStoreProps[key] = val;
- }
+ if (hashFunction) {
+ const storeHash = hashFunction({
+ state: this.env.store.state,
+ storeProps: storeProps,
+ revNumber,
+ deepRevNumber
+ });
+ if (storeHash !== this.__owl__.storeHash) {
+ didChange = true;
+ this.__owl__.storeHash = storeHash;
}
+ } else {
+ didChange = !shallowEqual(
+ storeProps,
+ this.__owl__.currentStoreProps
+ );
}
if (didChange) {
+ this.__owl__.currentStoreProps = storeProps;
this.updateProps(ownProps, false);
}
});
diff --git a/src/utils.ts b/src/utils.ts
index 89ea0d55..4f686290 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -113,6 +113,19 @@ export function findInTree>(
return null;
}
+export function shallowEqual(objA, objB) {
+ if (objA === objB) {
+ return true;
+ }
+ const keysA = Object.keys(objA);
+ for (let key of keysA) {
+ if (!(key in objB) || objA[key] !== objB[key]) {
+ return false;
+ }
+ }
+ return true;
+}
+
export function patch(C: any, patchName: string, patch: any) {
const proto = C.prototype;
if (!proto.__patches) {
diff --git a/tests/utils.test.ts b/tests/utils.test.ts
index b15ba167..980be636 100644
--- a/tests/utils.test.ts
+++ b/tests/utils.test.ts
@@ -5,6 +5,7 @@ import {
memoize,
debounce,
findInTree,
+ shallowEqual,
patch,
unpatch
} from "../src/utils";
@@ -94,6 +95,17 @@ describe("findInTree", () => {
});
});
+describe("shallowEqual", () => {
+ test("simple comparisons", () => {
+ const obj1 = {};
+ expect(shallowEqual(obj1, obj1)).toBe(true);
+ expect(shallowEqual({}, {})).toBe(true);
+ expect(shallowEqual({ a: 1 }, {})).toBe(false);
+ expect(shallowEqual({ a: 1 }, { a: 1 })).toBe(true);
+ expect(shallowEqual({ a: 1 }, ["a"])).toBe(false);
+ });
+});
+
describe("patch/unpatch", () => {
test("can monkey patch a class", () => {
class Test {