[REF] connected component: separate storeprops from props

closes #259
This commit is contained in:
Géry Debongnie
2019-08-23 17:34:29 +02:00
parent 2294c1967f
commit 34e463ad0a
2 changed files with 84 additions and 101 deletions
+59 -76
View File
@@ -12,19 +12,20 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
return env.store;
}
hashFunction: HashFunction = ({ storeProps }, options) => {
const observer = (this.__owl__ as any).store.observer;
let refFunction = this.deep ? observer.deepRevNumber : observer.revNumber;
if ("__owl__" in storeProps) {
return refFunction.call(observer, storeProps);
storeProps: any;
hashFunction: HashFunction = (storeProps, options) => {
const revFn = (this.__owl__ as any).revFn;
const rev = revFn(storeProps);
if (rev > 0) {
return rev;
}
const { currentStoreProps } = options;
let hash = 0;
for (let key in storeProps) {
const val = storeProps[key];
const hashVal = refFunction.call(observer, val);
const hashVal = revFn(val);
if (hashVal === 0) {
if (val !== currentStoreProps[key]) {
if (val !== options.prevStoreProps[key]) {
options.didChange = true;
}
} else {
@@ -37,32 +38,23 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
static mapStoreToProps(storeState, ownProps, getters) {
return {};
}
constructor(parent, props?: any) {
super(parent, props);
/**
* Need to do this here so 'deep' can be overrided by subcomponent easily
*/
async __prepareAndRender(scope?: Object, vars?: any): ReturnType<Component<any,any,any>["__prepareAndRender"]> {
const store = this.getStore(this.env);
const ownProps = this.props || {};
const storeProps = (<any>this.constructor).mapStoreToProps(
store.state,
ownProps,
store.getters
);
const mergedProps = Object.assign({}, ownProps, storeProps);
this.props = mergedProps;
(<any>this.__owl__).ownProps = ownProps;
(<any>this.__owl__).currentStoreProps = storeProps;
(<any>this.__owl__).store = store;
(<any>this.__owl__).storeHash = this.hashFunction(
{
state: store.state,
storeProps: storeProps
},
{
currentStoreProps: storeProps
}
);
this.storeProps = (<any>this.constructor).mapStoreToProps(store.state, ownProps, store.getters);
const observer = store.observer;
const revFn = this.deep ? observer.deepRevNumber : observer.revNumber;
(this.__owl__ as any).store = store;
(this.__owl__ as any).revFn = revFn.bind(observer);
(this.__owl__ as any).storeHash = this.hashFunction(this.storeProps, {
prevStoreProps: this.storeProps
});
(this.__owl__ as any).rev = observer.rev;
return super.__prepareAndRender(scope, vars);
}
/**
* We do not use the mounted hook here for a subtle reason: we want the
@@ -70,56 +62,47 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
* if we use the mounted hook, this will be done in the reverse order.
*/
__callMounted() {
(<any>this.__owl__).store.on("update", this, this.__checkUpdate);
(this.__owl__ as any).store.on("update", this, this.__checkUpdate);
super.__callMounted();
}
willUnmount() {
(<any>this.__owl__).store.off("update", this);
super.willUnmount();
__callWillUnmount() {
(this.__owl__ as any).store.off("update", this);
super.__callWillUnmount();
}
async __checkUpdate(updateId) {
if (updateId === (<any>this.__owl__).currentUpdateId) {
return;
}
const ownProps = (<any>this.__owl__).ownProps;
const storeProps = (<any>this.constructor).mapStoreToProps(
(<any>this.__owl__).store.state,
ownProps,
(<any>this.__owl__).store.getters
);
const options: any = {
currentStoreProps: (<any>this.__owl__).currentStoreProps
};
const storeHash = this.hashFunction(
{
state: (<any>this.__owl__).store.state,
storeProps: storeProps
},
options
);
let didChange = options.didChange;
if (storeHash !== (<any>this.__owl__).storeHash) {
didChange = true;
(<any>this.__owl__).storeHash = storeHash;
}
if (didChange) {
(<any>this.__owl__).currentStoreProps = storeProps;
await this.__updateProps(ownProps, false);
}
async __updateProps(nextProps: P, f, p, s, v) {
this.__updateStoreProps(nextProps);
return super.__updateProps(nextProps, f, p, s, v);
}
__updateProps(nextProps, forceUpdate, patchQueue?: any[]) {
const __owl__ = <any>this.__owl__;
__owl__.currentUpdateId = __owl__.store._updateId;
if (__owl__.ownProps !== nextProps) {
__owl__.currentStoreProps = (<any>this.constructor).mapStoreToProps(
__owl__.store.state,
nextProps,
__owl__.store.getters
);
__updateStoreProps(nextProps): boolean {
const store = (this.__owl__ as any).store;
const storeProps = (<any>this.constructor).mapStoreToProps(
store.state,
nextProps,
store.getters
);
const options = { prevStoreProps: this.storeProps, didChange: false };
const storeHash = this.hashFunction(storeProps, options);
this.storeProps = storeProps;
let didChange = options.didChange;
if (storeHash !== (this.__owl__ as any).storeHash) {
(this.__owl__ as any).storeHash = storeHash;
didChange = true;
}
(this.__owl__ as any).rev = store.observer.rev;
return didChange
}
async __checkUpdate() {
const observer = (this.__owl__ as any).store.observer;
if (observer.rev === (this.__owl__ as any).rev) {
// update was already done by updateProps, from parent
return;
}
const didChange = this.__updateStoreProps(this.props);
if (didChange) {
this.render();
}
__owl__.ownProps = nextProps;
const mergedProps = Object.assign({}, nextProps, __owl__.currentStoreProps);
return super.__updateProps(mergedProps, forceUpdate, patchQueue);
}
}
+25 -25
View File
@@ -20,7 +20,7 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="App">
<t t-foreach="props.todos" t-as="todo" >
<t t-foreach="storeProps.todos" t-as="todo" >
<Todo msg="todo.msg" t-key="todo"/>
</t>
</div>
@@ -56,7 +56,7 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="App">
<span t-foreach="props.todos" t-as="todo" t-key="todo">
<span t-foreach="storeProps.todos" t-as="todo" t-key="todo">
<t t-esc="todo.title"/>
</span>
</div>
@@ -104,7 +104,7 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="App">
<t t-foreach="props.todos" t-as="todo">
<t t-foreach="storeProps.todos" t-as="todo">
<Todo msg="todo.msg" t-key="todo" />
</t>
</div>
@@ -196,9 +196,9 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<span t-name="TodoItem"><t t-esc="props.text"/></span>
<span t-name="TodoItem"><t t-esc="storeProps.text"/></span>
<div t-name="TodoList">
<t t-foreach="props.todos" t-as="todo">
<t t-foreach="storeProps.todos" t-as="todo">
<TodoItem id="todo.id" t-key="todo.id"/>
</t>
</div>
@@ -247,11 +247,11 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="TodoItem">
<span><t t-esc="props.activeTodoText"/></span>
<span><t t-esc="props.importantTodoText"/></span>
<span><t t-esc="storeProps.activeTodoText"/></span>
<span><t t-esc="storeProps.importantTodoText"/></span>
</div>
<div t-name="TodoList">
<t t-foreach="props.todos" t-as="todo">
<t t-foreach="storeProps.todos" t-as="todo">
<TodoItem id="todo.id" t-key="todo.id"/>
</t>
</div>
@@ -287,7 +287,7 @@ describe("connecting a component to store", () => {
test("connected component is updated when props are updated", async () => {
env.qweb.addTemplates(`
<templates>
<span t-name="Beer"><t t-esc="props.name"/></span>
<span t-name="Beer"><t t-esc="storeProps.name"/></span>
<div t-name="App">
<Beer id="state.beerId"/>
</div>
@@ -322,7 +322,7 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="App">
<span t-foreach="props.beers" t-as="beer" t-key="beer.name"><t t-esc="beer.name"/></span>
<span t-foreach="storeProps.beers" t-as="beer" t-key="beer.name"><t t-esc="beer.name"/></span>
</div>
</templates>
`);
@@ -357,9 +357,9 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Beer">
<span>taster:<t t-esc="props.taster"/></span>
<span t-if="props.selected">selected:<t t-esc="props.selected.name"/></span>
<span t-if="props.consumed">consumed:<t t-esc="props.consumed.name"/></span>
<span>taster:<t t-esc="storeProps.taster"/></span>
<span t-if="storeProps.selected">selected:<t t-esc="storeProps.selected.name"/></span>
<span t-if="storeProps.consumed">consumed:<t t-esc="storeProps.consumed.name"/></span>
</div>
<div t-name="App">
<Beer id="state.beerId"/>
@@ -424,9 +424,9 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Beer">
<span>taster:<t t-esc="props.taster"/></span>
<span t-if="props.selected">selected:<t t-esc="props.selected.name"/></span>
<span t-if="props.consumed">consumed:<t t-esc="props.consumed.name"/></span>
<span>taster:<t t-esc="storeProps.taster"/></span>
<span t-if="storeProps.selected">selected:<t t-esc="storeProps.selected.name"/></span>
<span t-if="storeProps.consumed">consumed:<t t-esc="storeProps.consumed.name"/></span>
</div>
<div t-name="App">
<Beer id="state.beerId"/>
@@ -517,9 +517,9 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Parent">
<Child key="props.current"/>
<Child key="storeProps.current"/>
</div>
<span t-name="Child"><t t-esc="props.msg"/></span>
<span t-name="Child"><t t-esc="storeProps.msg"/></span>
</templates>
`);
@@ -577,13 +577,13 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="TodoApp" class="todoapp">
<t t-foreach="Object.values(props.todos)" t-as="todo">
<t t-foreach="Object.values(storeProps.todos)" t-as="todo">
<TodoItem t-key="todo.id" id="todo.id"/>
</t>
</div>
<div t-name="TodoItem" class="todo">
<t t-esc="props.todo.title"/>
<t t-esc="storeProps.todo.title"/>
<button class="destroy" t-on-click="editTodo">x</button>
</div>
</templates>
@@ -656,13 +656,13 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="TodoApp" class="todoapp">
<t t-foreach="Object.values(props.todos)" t-as="todo">
<t t-foreach="Object.values(storeProps.todos)" t-as="todo">
<TodoItem t-key="todo.id" id="todo.id"/>
</t>
</div>
<div t-name="TodoItem" class="todo">
<t t-esc="props.todo.title"/>
<t t-esc="storeProps.todo.title"/>
<button class="destroy" t-on-click="removeTodo">x</button>
</div>
</templates>
@@ -720,7 +720,7 @@ describe("connecting a component to store", () => {
env.qweb.addTemplates(`
<templates>
<div t-name="App"><t t-esc="props.msg"/></div>
<div t-name="App"><t t-esc="storeProps.msg"/></div>
</templates>
`);
@@ -829,11 +829,11 @@ describe("connected components and default values", () => {
<templates>
<div t-name="Message">
<t t-if="props.showId"><t t-esc="props.messageId"/></t>
<t t-esc="props.message.content"/>
<t t-esc="storeProps.message.content"/>
</div>
<div t-name="Thread">
<t t-if="props.showMessages">
<Message t-foreach="props.thread.messages" t-as="messageId" messageId="messageId" t-key="messageId"/>
<Message t-foreach="storeProps.thread.messages" t-as="messageId" messageId="messageId" t-key="messageId"/>
</t>
</div>
<div t-name="App"><Thread threadId="props.threadId"/></div>