-
Notifications
You must be signed in to change notification settings - Fork 400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(engine): refactor boundary protection for render phase #1507
fix(engine): refactor boundary protection for render phase #1507
Conversation
test case: A parent switches template and a child being disconnected sets a tracked property in disconnectedCallback
} | ||
const isEvaluatingTemplateInception = isEvaluatingTemplate; | ||
let vnodes: VNodes = []; | ||
runWithBoundaryProtection( |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@@ -124,6 +124,7 @@ export function invokeComponentRenderMethod(vm: VM): VNodes { | |||
() => { | |||
// job | |||
const html = callHook(component, render); | |||
isRendering = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@caridy Should we call this flag isRenderBeingInvoked
or something like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, feel free to rename it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this line can't be done here... because if the render() method throws, the state is never restored! also inception is not preserved when you assign the false...
() => { | ||
// invoke the selected template. | ||
vnodes = html.call(undefined, api, component, cmpSlots, context.tplCache!); | ||
const { styleVNode } = context; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this block should be outside of the protection.
} | ||
|
||
if (process.env.NODE_ENV !== 'production') { | ||
assert.invariant( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imagine that this throws an error... that will be captured by the boundary... that's not what we want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still not entirely convinced that this is safe.
@@ -609,10 +615,10 @@ export function ll( | |||
id: string, | |||
context?: (...args: any[]) => any | |||
): EventListener { | |||
const vmBeingRendered: VM | null = getVMBeingRendered(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should call this vm
as it was before, because it is using in the next turn where vmBeingRendered
is not longer set, it is confusing to call it like that.
} | ||
); | ||
return result || []; | ||
return isUndefined(html) ? [] : evaluateTemplate(vm, html); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could probably make this more clear, maybe adding a comment saying that html
can only be undefined if there is an error on render.call() that is captured by a boundary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: This PR introduces a slight inconsistency in the way we handle invalid templates returned by invoking render()
.
if the render() returns an undefined
- In dev mode, we assert and throw, invoke errorCallback in boundary protection
- In prod mode, we do not throw. We return a blank template to gracefully degrade
if the render() returns a null/string/non-function
- In dev mode, we assert and throw, invoke errorCallback in boundary protection
- In prod mode, we do not assert, the framework will encounter a TypeError while trying to use the return value like a function. We invoke boundary protection.
I feel, throwing in dev mode and returning a blank template in prod mode is better user experience. Want to hear opinions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not the same we have in master today... in master today, if you return undefined, in dev or prod, it throws.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in fact it is not about undefined, it is about a registered template.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor things... but it is looking good.
Benchmark resultsBase commit: lwc-engine-benchmark
|
1 similar comment
Benchmark resultsBase commit: lwc-engine-benchmark
|
This comment has been minimized.
This comment has been minimized.
@@ -609,10 +615,10 @@ export function ll( | |||
id: string, | |||
context?: (...args: any[]) => any | |||
): EventListener { | |||
if (isNull(vmBeingRendered)) { | |||
const vm: VM | null = getVMBeingRendered(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unnecessary type declaration for const.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, probably adding more tests to cover the cases of the original behavior, so we don't break it in the future.
Benchmark resultsBase commit: lwc-engine-benchmark
|
Will add tests for the original behavior when this change will be ported to master branch. |
* test: automation for issue#1506 test case: A parent switches template and a child being disconnected sets a tracked property in disconnectedCallback * fix: mark template evaluation as a separate global flag * fix: split boundary protection for render into two steps
* fix(engine): refactor boundary protection for render phase (#1507) * test: automation for issue#1506 test case: A parent switches template and a child being disconnected sets a tracked property in disconnectedCallback * fix: mark template evaluation as a separate global flag * fix: split boundary protection for render into two steps * test: testcase for an existing behavior test: run test only if global promise rejection handler is supported
Details
isRendering
.This PR separated the flag into two. One for render invocation and another for template evaluation.
Addresses issue #1506
Does this PR introduce breaking changes?
No, it does not introduce breaking changes.
If yes, please describe the impact and migration path for existing applications.
The PR fulfills these requirements: