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>
|
</label>
|
||||||
<button class="destroy" t-on-click="removeTodo"></button>
|
<button class="destroy" t-on-click="removeTodo"></button>
|
||||||
</div>
|
</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>
|
</li>
|
||||||
|
|
||||||
</templates>
|
</templates>
|
||||||
|
|||||||
@@ -319,10 +319,6 @@ export class Component<
|
|||||||
nextProps: Props,
|
nextProps: Props,
|
||||||
forceUpdate: boolean = false
|
forceUpdate: boolean = false
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (nextProps === this.__owl__.renderProps && !forceUpdate) {
|
|
||||||
await this.__owl__.renderPromise;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
const shouldUpdate = forceUpdate || this.shouldUpdate(nextProps);
|
||||||
return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve();
|
return shouldUpdate ? this._updateProps(nextProps) : Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-24
@@ -1,4 +1,5 @@
|
|||||||
import { EventBus } from "./event_bus";
|
import { EventBus } from "./event_bus";
|
||||||
|
import { shallowEqual } from "./utils";
|
||||||
import { Component } from "./component";
|
import { Component } from "./component";
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -235,17 +236,23 @@ export function makeObserver(): Observer {
|
|||||||
// Connect function
|
// Connect function
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
function setStoreProps(__owl__: any, storeProps: any) {
|
function revNumber<T extends Object>(o: T): number {
|
||||||
__owl__.currentStoreProps = storeProps;
|
if (!("__owl__" in o)) {
|
||||||
__owl__.currentStoreRevs = {};
|
return 0;
|
||||||
__owl__.currentStoreRev = storeProps.__owl__ && storeProps.__owl__.rev;
|
|
||||||
for (let key in storeProps) {
|
|
||||||
__owl__.currentStoreRevs[key] =
|
|
||||||
storeProps[key].__owl__ && storeProps[key].__owl__.rev;
|
|
||||||
}
|
}
|
||||||
|
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 function(Comp) {
|
||||||
return class extends Comp {
|
return class extends Comp {
|
||||||
constructor(parent, props?: any) {
|
constructor(parent, props?: any) {
|
||||||
@@ -254,32 +261,60 @@ export function connect(mapStateToProps) {
|
|||||||
const storeProps = mapStateToProps(env.store.state, ownProps);
|
const storeProps = mapStateToProps(env.store.state, ownProps);
|
||||||
const mergedProps = Object.assign({}, props || {}, storeProps);
|
const mergedProps = Object.assign({}, props || {}, storeProps);
|
||||||
super(parent, mergedProps);
|
super(parent, mergedProps);
|
||||||
setStoreProps(this.__owl__, storeProps);
|
|
||||||
this.__owl__.ownProps = ownProps;
|
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() {
|
mounted() {
|
||||||
this.env.store.on("update", this, () => {
|
this.env.store.on("update", this, () => {
|
||||||
const ownProps = this.__owl__.ownProps;
|
const ownProps = this.__owl__.ownProps;
|
||||||
const storeProps = mapStateToProps(this.env.store.state, ownProps);
|
const storeProps = mapStateToProps(this.env.store.state, ownProps);
|
||||||
let didChange = false;
|
let didChange = false;
|
||||||
if (
|
if (hashFunction) {
|
||||||
this.__owl__.currentStoreRev &&
|
const storeHash = hashFunction({
|
||||||
this.__owl__.currentStoreRev !== storeProps.__owl__.rev
|
state: this.env.store.state,
|
||||||
) {
|
storeProps: storeProps,
|
||||||
setStoreProps(this.__owl__, storeProps);
|
revNumber,
|
||||||
didChange = true;
|
deepRevNumber
|
||||||
} else {
|
});
|
||||||
const revs = this.__owl__.currentStoreRevs;
|
if (storeHash !== this.__owl__.storeHash) {
|
||||||
for (let key in storeProps) {
|
didChange = true;
|
||||||
const val = storeProps[key];
|
this.__owl__.storeHash = storeHash;
|
||||||
if (val.__owl__ && val.__owl__.rev !== revs[key]) {
|
|
||||||
didChange = true;
|
|
||||||
revs[key] = val.__owl__ && val.__owl__.rev;
|
|
||||||
this.__owl__.currentStoreProps[key] = val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
didChange = !shallowEqual(
|
||||||
|
storeProps,
|
||||||
|
this.__owl__.currentStoreProps
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (didChange) {
|
if (didChange) {
|
||||||
|
this.__owl__.currentStoreProps = storeProps;
|
||||||
this.updateProps(ownProps, false);
|
this.updateProps(ownProps, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -113,6 +113,19 @@ export function findInTree<T extends Tree<T>>(
|
|||||||
return null;
|
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) {
|
export function patch(C: any, patchName: string, patch: any) {
|
||||||
const proto = C.prototype;
|
const proto = C.prototype;
|
||||||
if (!proto.__patches) {
|
if (!proto.__patches) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
memoize,
|
memoize,
|
||||||
debounce,
|
debounce,
|
||||||
findInTree,
|
findInTree,
|
||||||
|
shallowEqual,
|
||||||
patch,
|
patch,
|
||||||
unpatch
|
unpatch
|
||||||
} from "../src/utils";
|
} 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", () => {
|
describe("patch/unpatch", () => {
|
||||||
test("can monkey patch a class", () => {
|
test("can monkey patch a class", () => {
|
||||||
class Test {
|
class Test {
|
||||||
|
|||||||
Reference in New Issue
Block a user