react-native icon indicating copy to clipboard operation
react-native copied to clipboard

decouple commit from mount on Android

Open sammy-SC opened this issue 1 year ago • 1 comments

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:

  1. View preallocation on Android: https://fburl.com/code/r62p3vot
  2. 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:

  1. React issues asks Fabric to create new shadow node A with background colour red.
  2. Fabric asks Android to allocate a view for shadow node A with background colour red.
  3. 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.
  4. React clones node A, changing the background colour to blue and commits the new tree T2.
  5. 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:

  1. Ignore view preallocation for shadow nodes which were cloned with new props.
  2. 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

sammy-SC avatar Apr 24 '24 12:04 sammy-SC

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

analysis-bot avatar Apr 24 '24 13:04 analysis-bot

This pull request has been merged in facebook/react-native@cf926a1329a8cd6921e9799877883ac1fbc26fbb.

facebook-github-bot avatar Apr 24 '24 21:04 facebook-github-bot

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?

github-actions[bot] avatar Apr 24 '24 21:04 github-actions[bot]