decouple commit from mount on Android
Summary:
Changelog:
[Android] [Fixed] - fix a case when view preallocation or props forwarding on Android lead to dropped props update
What does this fix
This fixes a bug where prop change is not delivered to Android mounting layer if the prop change was initiated from state update inside of useLayoutEffect, componentDidMount or componentDidUpdate.
This affects android only and when batched rendering is enabled.
There are two root causes of this problem:
- View preallocation on Android: https://fburl.com/code/r62p3vot
- Prop forwarding on Android: https://fburl.com/code/644f1ppk
Minimal repro :
import React, {useLayoutEffect, useState} from 'react';
import {Button, SafeAreaView, View} from 'react-native';
function Foo() {
const [bgColor, setBgColor] = React.useState('red');
useLayoutEffect(() => {
console.log('useLayoutEffect');
setBgColor('blue');
}, []);
return (
<View
style={{
backgroundColor: bgColor,
width: '100%',
height: '100%',
}}
/>
);
}
function RNTesterApp() {
const [show, setShow] = useState(false);
return (
<SafeAreaView>
<Button title="Toggle" onPress={() => setShow(!show)} />
{show && <Foo />}
</SafeAreaView>
);
}
export default RNTesterApp;
The underlaying problem
The problem is combination of view preallocation and batched rendering updates.
Here is a step by step what happens in the repro above:
- React issues asks Fabric to create new shadow node A with background colour red.
- Fabric asks Android to allocate a view for shadow node A with background colour red.
- React commits tree T1 and calls layout effects. Meanwhile Fabric waits, without trying to mount the tree T1, to prevent painting state that is about to be updated and prevent flickering.
- React clones node A, changing the background colour to blue and commits the new tree T2.
- Fabric, will now go ahead and mount the latest tree T2. While creating mount instructions, it will drop prop updates because it believes prop updates where delivered already as part of step 2.
The fix
The fix is to change two things:
- Ignore view preallocation for shadow nodes which were cloned with new props.
- Set hasBeenMounted flag on ShadowNode later in the Fabric pipeline to fix it.
Both of these are hidden behind a single feature flag: fixMountedFlagAndFixPreallocationClone
Performance implication:
I estimate that this will impact around 3% of views.
Reviewed By: rubennorte
Differential Revision: D56353589
| Platform | Engine | Arch | Size (bytes) | Diff |
|---|---|---|---|---|
| android | hermes | arm64-v8a | 19,410,823 | -199 |
| android | hermes | armeabi-v7a | n/a | -- |
| android | hermes | x86 | n/a | -- |
| android | hermes | x86_64 | n/a | -- |
| android | jsc | arm64-v8a | 22,784,355 | +11 |
| android | jsc | armeabi-v7a | n/a | -- |
| android | jsc | x86 | n/a | -- |
| android | jsc | x86_64 | n/a | -- |
Base commit: 849da2146ca27b7512f2f3ea0f8af8aa001e807a Branch: main
This pull request has been merged in facebook/react-native@cf926a1329a8cd6921e9799877883ac1fbc26fbb.
This pull request was successfully merged by @sammy-SC in cf926a1329a8cd6921e9799877883ac1fbc26fbb.
When will my fix make it into a release? | How to file a pick request?