mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] store: connect function takes hash function in param
With this commit, we can now decides precisely if a component should be rerendered, by using a hash function. By default, the hash function will deep observe each store element coming from the result of the mapStateToProps. closes #24
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
</label>
|
||||
<button class="destroy" t-on-click="removeTodo"></button>
|
||||
</div>
|
||||
<input class="edit" t-ref="input" t-if="state.isEditing" t-att-value="props.title" t-on-keyup="handleKeyup" t-on-blur="handleBlur"/>
|
||||
<input class="edit" t-ref="'input'" t-if="state.isEditing" t-att-value="props.title" t-on-keyup="handleKeyup" t-on-blur="handleBlur"/>
|
||||
</li>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -319,10 +319,6 @@ export class Component<
|
||||
nextProps: Props,
|
||||
forceUpdate: boolean = false
|
||||
): Promise<void> {
|
||||
if (nextProps === this.__owl__.renderProps && !forceUpdate) {
|
||||
await this.__owl__.renderPromise;
|
||||
return;
|
||||
}
|
||||
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
||||
return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve();
|
||||
}
|
||||
|
||||
+59
-24
@@ -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<T extends Object>(o: T): number {
|
||||
if (!("__owl__" in o)) {
|
||||
return 0;
|
||||
}
|
||||
return (<any>o).__owl__.rev;
|
||||
}
|
||||
|
||||
export function connect(mapStateToProps) {
|
||||
function deepRevNumber<T extends Object>(o: T): number {
|
||||
if (!("__owl__" in o)) {
|
||||
return 0;
|
||||
}
|
||||
return (<any>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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -113,6 +113,19 @@ export function findInTree<T extends Tree<T>>(
|
||||
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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user