react / react/react-native

The case for possibility of triggering synchronous layout from UI thread

Ouverte
#51,815 4 commentaires 2 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Needs: Triage :mag: p: Software Mansion Partner Type: New Architecture
Langage dominant
C++
Étoiles
127k
Forks
25.3k
Merge moyen
1 j 23 h
PR mergées (30 j)
4

Description

Description

Follow up to #51809.

This issue report is about lack of support for synchronous layout on UI thread, that is necessary to expose / implement certain platform components, e.g. bottom sheets, without "flicker effect"
(see attached recording).

In current state of Fabric, we, in react-native-screens need to go for ugly & not really working workarounds.

To reproduce the problem it is enough to run the application on iOS (the issue applies also to Android)
& drag the sheet upwards.

Please note that the attached reproduction is minimal - in sense it isn't exactly what we're doing in react-native-screens .
We're not sending an event through event emitter, but rather we update the component's shadow node state - this is also asynchronous and leads to the same effect.

Error mechanism

I'll describe it on iOS example, however the same case can be built for Android.

This is kinda simplified & assumes just one possible order of execution, but should be enough to explain the problem.

Let's consider scenario where we drag the sheet up (see recording to get the context), and the sheet has starting height h1.

  1. User tries to drag the sheet up,
  2. UIKit receives touch & among many things native layout is triggered,
  3. The sheet is resized (height goes up to some h2),
  4. We send asynchronous event (in library code we do an asynchronous shadow node state update),
  5. User continues to drag the sheet up,
  6. Another native sheet resize, sheet has some height h3 now,
  7. React updates state & starts render,
  8. User continues to drag the sheet up,
  9. Another native sheet resize, sheet has some height h4 now,
  10. React render & commit completes & mount is scheduled & let's say executed,
  11. SheetView receives layout computed for the moment sheet had height h2, despite the fact the sheet is already h4.
  12. h4 > h1 - hence the flicker.

I went with sending direct event here, just to simplify the reproduction, but if you need exact setup react-native-screens has, let me know I'll setup custom shadow node for SheetView component.

Motivation

The motivation from #51809 applies.

Additionally, need to be able to do this, e.g. in order to let users implement footer components that pair up with the sheet, or allow for content resizing just as in the reproduction.

Possible solution approach

We need the ability to trigger synchronous state update together with commit, layout & later mount from the UI thread for the high priority UI events, to keep
the interface responsive and in sync with native components, which are crucial for "native app experience".

I know this kinda is behind feature flag
right now & some work is being done around this & together with applying appropriate commit options we would be able to achieve synchronous commit & mount.

Bringing it to your attention to emphasize the need for this feature. At the same time from the perspective of native lib dev want to say that it would be great
to have ability to choose whether state update is synchronous or not (not all updates need to block the UI thread).

Steps to reproduce
  1. Run the app on iOS (the reproduction is set up for that platform)
  2. Drag the sheet up
  3. Observe the flicker
React Native Version

0.79.2

Affected Platforms

Runtime - Android, Runtime - iOS

Areas

Fabric - The New Renderer

Output of npx @react-native-community/cli info
System:
  OS: macOS 15.5
  CPU: (14) arm64 Apple M4 Pro
  Memory: 121.70 MB / 24.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 23.7.0
    path: /opt/homebrew/bin/node
  Yarn:
    version: 1.22.19
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.9.2
    path: /opt/homebrew/bin/npm
  Watchman: Not Found
Managers:
  CocoaPods:
    version: 1.16.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.4
      - iOS 18.4
      - macOS 15.4
      - tvOS 18.4
      - visionOS 2.4
      - watchOS 11.4
  Android SDK:
    API Levels:
      - "21"
      - "29"
      - "30"
      - "31"
      - "32"
      - "33"
      - "34"
      - "35"
    Build Tools:
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 33.0.1
      - 34.0.0
      - 35.0.0
    System Images:
      - android-29 | Google Play ARM 64 v8a
      - android-31 | Google Play ARM 64 v8a
      - android-33 | Google Play ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
      - android-34 | Google Play ARM 64 v8a
      - android-35 | Google APIs ARM 64 v8a
      - android-35 | Google Play ARM 64 v8a
      - android-35 | Pre-Release 16 KB Page Size Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2024.2 AI-242.23726.103.2422.13016713
  Xcode:
    version: 16.3/16E140
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.2
    path: /usr/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 18.0.0
    wanted: 18.0.0
  react:
    installed: 19.0.0
    wanted: 19.0.0
  react-native:
    installed: 0.79.2
    wanted: 0.79.2
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: true
Stacktrace or Logs
N/A
MANDATORY Reproducer

https://github.com/kkafar/layout-issues/tree/%40kkafar/ui-layout-issue <-- please notice the branch!

Screenshots and Videos

https://github.com/user-attachments/assets/f226ec07-7891-4d7e-a55f-b3c885c73fbd

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Reproduisez le scintillement avec la branche layout-issues obligatoire en exécutant l’application iOS et en faisant glisser la feuille vers le haut. Lisez ensuite packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp ainsi que le chemin de commit Fabric associé. C’est terminé lorsque les événements d’interface utilisateur hautement prioritaires peuvent opter pour une mise à jour d’état, un commit, un layout et un mount synchrones sur iOS et Android sans que la feuille ne soit désynchronisée.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
cpp, react-native
Domaine
mobile-dev
Type d'issue
Fonctionnalité
Difficulté
5/5
Temps estimé
Plus d'une semaine
Activité
Calme
Clarté
Plutôt claire
Accessibilité débutants
35/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.