[FIX] store: properly call onUpdate functions in some cases

Before this commit, the following scenario could happen:

Suppose that we have a parent component A, connected to a store,
 and a child component B, also connected to the store and using
the onUpdate feature.

Then, we remount the A component in some other places and a
rendering is initiated in A.  We immediately update the store state.
What happens next is:

- rendering A is done, A internal revid is updated
- store update A (but nothing is done because the state change here
  does not modify A)
- rendering B is done (from parent), B internal revid is updated
- store update B, notice internal revid is updated, does not call the
  onUpdate function

We then have the B component which has not its internal state updated,
because we did not call its onUpdate function.

The solution is to move the onUpdate call in a "preupdate" event, to be
sure that it is called everytime the store is updated.

closes #816
This commit is contained in:
Géry Debongnie
2021-01-07 12:36:46 +01:00
committed by aab-odoo
parent d043d47754
commit 4a96eff3c6
2 changed files with 70 additions and 5 deletions
+14 -3
View File
@@ -75,6 +75,11 @@ export class Store extends Context {
);
return result;
}
__notifyComponents(): Promise<void> {
this.trigger("before-update");
return super.__notifyComponents();
}
}
interface SelectorOptions {
@@ -105,13 +110,16 @@ export function useStore(selector, options: SelectorOptions = {}): any {
const newRevNumber = hashFn(result);
if ((newRevNumber > 0 && revNumber !== newRevNumber) || !isEqual(oldResult, result)) {
revNumber = newRevNumber;
if (options.onUpdate) {
options.onUpdate(result);
}
return true;
}
return false;
}
if (options.onUpdate) {
store.on("before-update", component, () => {
const newValue = selector(store!.state, component.props!);
options.onUpdate(newValue);
});
}
store.updateFunctions[componentId].push(function (): boolean {
return selectCompareUpdate(store!.state, component.props);
});
@@ -132,6 +140,9 @@ export function useStore(selector, options: SelectorOptions = {}): any {
const __destroy = component.__destroy;
component.__destroy = (parent) => {
delete store.updateFunctions[componentId];
if (options.onUpdate) {
store.off("before-update", component);
}
__destroy.call(component, parent);
};