fix(core): Refresh transplanted views at insertion point only (#35968)

Only refresh transplanted views at the insertion location in Ivy.
Previously, Ivy would check transplanted views at both the insertion and
declaration points. This is achieved by adding a marker to the insertion
tree when we encounter a transplanted view that needs to be refreshed at
its declaration. We use this marker as an extra indication that we still
need to descend and refresh those transplanted views at their insertion
locations even if the insertion view and/or its parents are not dirty.

This change fixes several issues:

  * Transplanted views refreshed twice if both insertion and declaration
  are dirty. This could be an error if the insertion component changes
  result in data not being available to the transplanted view because it
  is slated to be removed.
  * CheckAlways transplanted views not refreshed if shielded by
  non-dirty OnPush (fixes #35400)
  * Transplanted views still refreshed when insertion tree is detached
  (fixes #21324)

PR Close #35968
This commit is contained in:
Andrew Scott
2020-01-13 15:12:16 -08:00
committed by Andrew Kushnir
parent e5f459d32b
commit c8c2272a9f
12 changed files with 859 additions and 274 deletions

View File

@ -13,7 +13,7 @@ import {LContext, MONKEY_PATCH_KEY_NAME} from '../interfaces/context';
import {TConstants, TNode} from '../interfaces/node';
import {isProceduralRenderer, RNode} from '../interfaces/renderer';
import {isLContainer, isLView} from '../interfaces/type_checks';
import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, RENDERER, TData, TView} from '../interfaces/view';
import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, RENDERER, TData, TRANSPLANTED_VIEWS_TO_REFRESH, TView} from '../interfaces/view';
@ -194,4 +194,24 @@ export function getLContainerActiveIndex(lContainer: LContainer) {
export function setLContainerActiveIndex(lContainer: LContainer, index: number) {
lContainer[ACTIVE_INDEX] = index << ActiveIndexFlag.SHIFT;
}
}
/**
* Updates the `TRANSPLANTED_VIEWS_TO_REFRESH` counter on the `LContainer` as well as the parents
* whose
* 1. counter goes from 0 to 1, indicating that there is a new child that has a view to refresh
* or
* 2. counter goes from 1 to 0, indicating there are no more descendant views to refresh
*/
export function updateTransplantedViewCount(lContainer: LContainer, amount: 1|- 1) {
lContainer[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
let viewOrContainer: LView|LContainer = lContainer;
let parent: LView|LContainer|null = lContainer[PARENT];
while (parent !== null &&
((amount === 1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 1) ||
(amount === -1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 0))) {
parent[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
viewOrContainer = parent;
parent = parent[PARENT];
}
}