[FIX] ConnectedComponent: keep props uptodate

This is a tricky bug. The problem is that a ConnectedComponent can
trigger a rendering before it is aware that there is a state change in
the store, and just before the store update event comes in.

What we do in this commit is to update the storeProps whenever a
rendering is scheduled.  However, we need to keep the rendering
information as a promise to give it in some case to the __checkUpdate
method (see the note in the render method)

closes #268
This commit is contained in:
Géry Debongnie
2019-09-04 15:12:46 +02:00
parent 4edb90f44b
commit c52799de2c
3 changed files with 125 additions and 9 deletions
@@ -15,3 +15,7 @@ exports[`connecting a component to store deep and shallow connecting a component
exports[`connecting a component to store deep and shallow connecting a component 3`] = `"<div><span>Bertinchamps</span></div>"`;
exports[`connecting a component to store deep and shallow connecting a component 4`] = `"<div><span>Kasteel</span></div>"`;
exports[`various scenarios scenarios with async store updates and some components events 1`] = `"<div><button>Do stuff</button><div><span>Attachment 100</span><span>Name: text.txt</span></div></div>"`;
exports[`various scenarios scenarios with async store updates and some components events 2`] = `"<div><button>Do stuff</button></div>"`;
+82
View File
@@ -1094,3 +1094,85 @@ describe("connected components and default values", () => {
expect(fixture.innerHTML).toBe("<div>1</div>");
});
});
describe("various scenarios", () => {
let fixture: HTMLElement;
let env: Env;
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
});
afterEach(() => {
fixture.remove();
});
test("scenarios with async store updates and some components events", async () => {
const actions = {
async deleteAttachment({ state }) {
await Promise.resolve();
delete state.attachments[100];
state.messages[10].attachmentIds = [];
}
};
const state = {
attachments: {
100: {
id: 100,
name: "text.txt"
}
},
messages: {
10: {
attachmentIds: [100],
id: 10
}
}
};
const store = new Store({ actions, state });
env.qweb.addTemplates(`
<templates>
<div t-name="Message">
<button t-on-click="doStuff">Do stuff</button>
<Attachment t-foreach="storeProps.attachmentIds" t-key="attachmentId" t-as="attachmentId" id="attachmentId"/>
</div>
<div t-name="Attachment">
<span>Attachment <t t-esc="props.id"/></span>
<span>Name: <t t-esc="storeProps.name"/></span>
</div>
</templates>
`);
class Attachment extends ConnectedComponent<any, any, any> {
static mapStoreToProps(state, ownProps) {
return {
name: state.attachments[ownProps.id].name
};
}
}
class Message extends ConnectedComponent<any, any, any> {
static mapStoreToProps(state) {
return {
attachmentIds: state.messages[10].attachmentIds
};
}
components = { Attachment };
state = { isAttachmentDeleted: false };
doStuff() {
this.dispatch("deleteAttachment", 100);
this.state.isAttachmentDeleted = true;
}
}
(<any>env).store = store;
const message = new Message(env);
await message.mount(fixture);
expect(fixture.innerHTML).toMatchSnapshot();
fixture.querySelector("button")!.click();
await nextTick();
expect(fixture.innerHTML).toMatchSnapshot();
});
});