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
Before this commit, an unwanted behaviour happened when using components
with shouldUpdate implemented, and store/state.
The actual problem is the following: the sub component is using a store,
and shouldupdate. Whenever the component is rendered, it checks if there is an incoming
rendering from the context. If that is the case, it skips the
rendering, because we actually only want to be rendered by the store
rendering (otherwise, we may run into issue with inconsistent data (more
recent data from the context, older data in the component).
However, if shouldUpdate is implemented, then the rendering coming from
the context simply does not arrive.
To fix this, we tried to just force these renderings to go through all children,
regardless of their shouldUpdate status. This actually works, but then
shouldUpdate is ignored, which is an issue in Odoo discuss.
Then, after discussing this situation, we noticed that the context/store
system is actually unsafe: the protection given by the check mentioned
above is in fact fundamentally insufficient: there are other perfectly
valid situations where a rendering can be triggered on the component,
which will bypass the check (for example, an explicit call to
this.render() or a rendering initiated by some parent component) and
cause a crash if the component is not properly defensively written.
Therefore, it is currently mandatory for all components using
context/store to be aware of that, and to protect themselves against
such situations. So, in that regard, the check is not really a
protection, it just helps hiding an unsafe situation anyway and we
decided to remove it.
Note that this is a potentially breaking change: components using a
store and some local state will now be rendered twice in some cases...
closes#799
Before this commit, Env was an indexed type, this means that one could
write env.anything, and it would accept it as a valid type. This is
actually quite dangerous, because we lose the typing advantages for all
keys that are properly defined.
For example, if a component is defined as:
class MyComponent extends Component<Props> {
...
}
Then Typescript will let it use anything from the environment, even if
it is wrong. So, most properly typed Typescript applications should use
instead a sub environment:
interface MyAppEnv extends Env {
someKey: someValue
}
Then, the component should be defined this way:
class MyComponent extends Component<Props, MyAppEnv> {
...
}
Before this commit, any typos in the environment accesses would not be
noticed by typescript.
In most cases, we just want Component<any, Env>. But since it was so
annoying to have always the type Env, we actually used
Component<any,any> everywhere.
With this commit, the generic types have a default (and their order is
swapped), so we can simply use Component in most cases, and
Component<Props> when we want to type the props.
It now takes two arguments: parent (optional, only for non-root
components) and props (optional). In the case of the root component,
the env is taken from the config (config.defaultEnv). If it doesn't
exists, the default env is created on the fly.
Closes#306