Previously, we were wrapping the entire renderFn in a try/catch, causing
errors during template execution to be caught and wrapped by
onWillRender/onRendered which is undesirable. Now we only wrap the hook
that's being registered.
This allows to see which objects and which keys in those objects a
component is observing, so that issues with missing renders or extra
renders can be more easily diagnosed.
With this commit, component only render child
components if they have different props (shallow
equality). Otherwise, we trust the reactivity
system to make sure that all impacted components
are updated
Previously, a crash in a lifecycle hook for any reason would throw an
error whose stack trace started from the scheduler and contained only
the place where the hook was called by owl, but not the place where the
hook was registered by the user. This proved very difficult for users to
debug as they cannot really tell which component registered that hook.
This commit alleviates the issue by creating a new Error when the hook
is originally called, and wrapping the registered callback in a try
catch, throwing an error with the correct stack trace instead of the
error in the hook, and setting the error in the hook as the cause of
this synthetic error.
Have a GrandParent which controls whether one of its GrandChildren is displayed or not.
First, the GrandChild is displayed. Then, change the state of the GrandParent in order to kill
the GrandChild.
Before this commit the GrandChild is only removed from the DOM, as bdom correctly works.
But it is not destroyed.
After this commit, the GrandChild is correctly destroyed.
Every use case involving some sort of key set on a component would give birth to a leak in an async context:
- If a key of a component changed, the outdated one was never destroyed.
- destroyed component were never removed from their parent's reference map.
This commit solves both issues, that are tightly linked anyway.
Have a component which does a render in its onWillPatch, onPatched, onMounted hooks.
Before this commit, the result was incorrect: the second rendering was not taken into account.
After this commit, those renderings are correctly applied at the price of a delayed render when the fiber
is in a critical state.
Owl provides a way to manage errors occuring in component lifecycle
methods. However, before this commit, these errors were not always
logged or visible, which is very annoying in the common developer
workflow (doing something, checking it works, seeing no error but a
broken interface).
In this commit, we make sure errors are logged/throws in all cases:
- if an error occurs in a mounting operation => the promise is rejected
(which will log the error)
- if an error occurs after the mounting operation and is not handled by
any error handlers => the error will be logged (with console.error).
Also, in that case, this commit adds a warning to explain that owl
destroys the root component, which will help developers understanding
what happened.
Have a child component on which a render is triggered.
This component delays its willUpdateProps and makes a rendering during the willUpdateProps
Before this commit, renderings of the child were inconsistent across
its parent's renderings.
After this commit, it works as expected.
Before this commit, class inheritance when using the onError hook was unclear nay wrong.
After this commit, error handlers are called from the bottom up in the inheritance hierarchy.
If a handler doesn't rethrow the error, the handling stops there and no other handler is called.
If a handler does rethrow, the handlers declared in a parent class are executed.
In the following situation: A parent of B, B parent of C, with an error
when C is mounted, caught by B and retriggering a rendering in B, then
the onMounted hook of A wasn't properly called. This commit fixes this
problem.
When a parent and a child were rendered at the same time, it was
possible for the 2 renders to decrement the same fiber internal
counter, which meant that the render was stalled.
The slot inner working has been reworked. A prop "slots" is now passed
explicitely to the component. It looks like
{ slotName_1: slotInfo_1, ..., slotName_m: slotInfo_m }
with the objects slotInfo_i with mandatory keys "__render", "__ctx",
and optional key "__scope" and possibly others.
Here is how a slotInfo object can be created:
A slotInfo object is normally created by setting in a template something
like
<div>
<t t-set-slot="foo" t-set-scope="scope" param_1="var" param_2="3">
content
<t t-esc="scope.bool"/>
<t t-esc="scope.num"/>
</t>
</div>
and it will be used somewhere like
<div>
<t t-esc="props.slots.foo.param_1"/>
<t t-slot="foo" bool="other_var" num="5">
</div>
In the above example, the function "__render" produces the block dom
element for the content of the t-set-slot.
The context "__ctx" will have a key "scope" with value { bool: ..., num: 5 }
and "__scope" will be set to "scope".
This commit makes the reactivity system more fine grained and makes it
more eager to stop observing keys or objects when they are modified,
this results in fewer "false positive" notifications.
Before this commit, errors triggered at the level of the fiber (as opposed to at the level
of a component's rendering), were handled as the very top level of the rendering, that is,
in the scheduler.
This was wrong because components below in the rendering tree would not have a chance to handle their
children's or their own errors.
After this commit, error triggered in willPatch, onMounted and onPatched are correctly handled
at the closest component to where they were thrown.
Have a Child Compnent which has one component that succeeds and another
one that fails at its instanciation.
The Child component handles the Errors by rendering itself.
Before this commit, the error handling algorithm made impossible for the scheduler to finish.
This was because the current fiber was still counted as ongoing, when it was actually completed.
After this commit, this use case is handled correctly.