Fixed Android bug causing to remove wrong view after reordering by zIndex #10951
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Commit 3d3b067 implemented zIndex support for Android. However after zIndex resorting of the views, the JS doesn't know about the new order. When dynamically removing the view from an array of views, JS sends the command to remove the view at certain index, which is not the same in Java anymore since the order of items changed.
This issue is also happens when adding view after reordering - it's added in the wrong position instead, but since it gets reordered again, it doesn't have visible effect, that's why it's only spotted when removing views.
Test Plan:
Related issue and the simple empty react-native 0.37 app where it was tested: #8968
Test app code:
Starting screen:
Before fix, when trying to remove green, it removes blue instead:
After fix it removed green correctly:
When clicking toggle again, it adds green back, the blue is still ordered correctly by zIndex:
Initially the order of operations in
manageChildren
inNativeViewHierarchyManager.java
was:indicesToRemove
, callviewManager.removeViewAt(viewToManage, indexToRemove)
. However if the view is animated and it's ID in the array oftagsToDelete
, do nothing and delegate this job of removing item to the step 3).viewsToAdd
, add the views.tagsToDelete
, perform animations on the views that are using animations, then callviewManager.removeView(viewToManage, viewToDestroy)
anddropView(viewToDestroy)
. But if the view is not animated, onlydropView(viewToDestroy)
is called because we assume that correct view was previously removed in step 1)New order of operations:
tagsToDelete
, perform animations if necessary, then callviewManager.removeView(viewToManage, viewToDestroy)
anddropView(viewToDestroy)
. If no animations, just callviewManager.removeView(viewToManage, viewToDestroy)
anddropView(viewToDestroy)
right away.viewsToAdd
, add the views.The intent is to perform clean-up by only looping by
tagsToDelete
first, instead of splitting the clean-up into separate steps of removing by index, then dropping by tagToDelete. And then when all the views are properly removed, we add the new views. SinceindicesToRemove
it's not reliable anymore and we don't use it forremoveViewAt
, we can get rid of the whole 1) step and replace it by 3) step.Also by having the old operations order we having the case that the wrong view is removed in step 1), but then, correct view is dropped in step 3). In result we have inconsistency when removing one view but dropping another. It's described in more details in mentioned above related issue comments.
Because the diff doesn't look very clear because of replacing the blocks of code, here is what's actually changed:
indicesToRemove
as it's not reliable anymore and causing wrong view to be removed.tagsToDelete
before adding new viewsviewManager.removeView(viewToManage, viewToDestroy)
inelse
statement.