Skip to content

Commit 366da8c

Browse files
mdvaccafacebook-github-bot
authored andcommitted
Fail gracefully when a component is not registered in production
Summary: This diff implements a mechanism to gracefully fail when a component is not registered in production changelog: [internal] internal Reviewed By: fkgozali Differential Revision: D74276800
1 parent 2ed67cb commit 366da8c

File tree

4 files changed

+44
-6
lines changed

4 files changed

+44
-6
lines changed

packages/react-native/ReactAndroid/api/ReactAndroid.api

+1
Original file line numberDiff line numberDiff line change
@@ -4708,6 +4708,7 @@ public abstract interface class com/facebook/react/uimanager/ViewManagerProperty
47084708
}
47094709

47104710
public final class com/facebook/react/uimanager/ViewManagerRegistry : android/content/ComponentCallbacks2 {
4711+
public static final field TAG Ljava/lang/String;
47114712
public fun <init> (Lcom/facebook/react/uimanager/ViewManagerResolver;)V
47124713
public fun <init> (Ljava/util/List;)V
47134714
public fun <init> (Ljava/util/Map;)V

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.java

+26-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@
1010
import android.content.ComponentCallbacks2;
1111
import android.content.res.Configuration;
1212
import androidx.annotation.Nullable;
13+
import com.facebook.common.logging.FLog;
1314
import com.facebook.infer.annotation.Nullsafe;
15+
import com.facebook.react.bridge.ReactSoftExceptionLogger;
1416
import com.facebook.react.bridge.UiThreadUtil;
1517
import com.facebook.react.common.MapBuilder;
18+
import com.facebook.react.common.build.ReactBuildConfig;
19+
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags;
1620
import java.util.ArrayList;
1721
import java.util.List;
1822
import java.util.Map;
@@ -24,6 +28,7 @@
2428
@Nullsafe(Nullsafe.Mode.LOCAL)
2529
public final class ViewManagerRegistry implements ComponentCallbacks2 {
2630

31+
public static final String TAG = "ViewManagerRegistry";
2732
private final Map<String, ViewManager> mViewManagers;
2833
private final @Nullable ViewManagerResolver mViewManagerResolver;
2934

@@ -76,14 +81,33 @@ public synchronized ViewManager get(String className) {
7681
viewManager = getViewManagerFromResolver(rctViewManagerName);
7782
if (viewManager != null) return viewManager;
7883

79-
throw new IllegalViewOperationException(
84+
String errorMessage =
8085
"Can't find ViewManager '"
8186
+ className
8287
+ "' nor '"
8388
+ rctViewManagerName
8489
+ "' in ViewManagerRegistry"
8590
+ ", existing names are: "
86-
+ mViewManagerResolver.getViewManagerNames());
91+
+ mViewManagerResolver.getViewManagerNames();
92+
// In release mode we don't want to crash the app if the view manager is not found.
93+
// Instead we return a dummy view manager that will render an empty view (including children)
94+
// and log an error.
95+
if (!ReactBuildConfig.DEBUG
96+
&& ReactNativeFeatureFlags.enableGracefulUnregisteredComponentFailureAndroid()) {
97+
// 1. Log the error
98+
FLog.e(TAG, errorMessage);
99+
ReactSoftExceptionLogger.logSoftException(
100+
ReactSoftExceptionLogger.Categories.SOFT_ASSERTIONS,
101+
new IllegalStateException(errorMessage));
102+
103+
// 2. Render UnimplementedNativeView if found.
104+
viewManager = getViewManagerFromResolver("UnimplementedNativeView");
105+
if (viewManager != null) {
106+
return viewManager;
107+
}
108+
}
109+
110+
throw new IllegalViewOperationException(errorMessage);
87111
}
88112
throw new IllegalViewOperationException("No ViewManager found for class " + className);
89113
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/ReactUnimplementedView.kt

+10-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ import android.view.Gravity
1313
import android.widget.LinearLayout
1414
import androidx.appcompat.widget.AppCompatTextView
1515
import com.facebook.react.common.build.ReactBuildConfig
16+
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
1617

17-
internal class ReactUnimplementedView(context: Context) : LinearLayout(context) {
18+
internal class ReactUnimplementedView(
19+
context: Context,
20+
private val onError: ((message: String) -> Unit)
21+
) : LinearLayout(context) {
1822

1923
private val textView: AppCompatTextView = AppCompatTextView(context)
2024

@@ -33,8 +37,12 @@ internal class ReactUnimplementedView(context: Context) : LinearLayout(context)
3337
}
3438

3539
internal fun setName(name: String) {
40+
val errorMessage = "'$name' is not registered."
3641
if (ReactBuildConfig.DEBUG) {
37-
textView.text = "'$name' is not registered."
42+
textView.text = errorMessage
43+
}
44+
if (ReactNativeFeatureFlags.enableGracefulUnregisteredComponentFailureAndroid()) {
45+
onError(errorMessage)
3846
}
3947
}
4048
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/unimplementedview/ReactUnimplementedViewManager.kt

+7-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package com.facebook.react.views.unimplementedview
99

10+
import com.facebook.common.logging.FLog
1011
import com.facebook.react.module.annotations.ReactModule
1112
import com.facebook.react.uimanager.ThemedReactContext
1213
import com.facebook.react.uimanager.ViewGroupManager
@@ -20,7 +21,11 @@ import com.facebook.react.viewmanagers.UnimplementedNativeViewManagerInterface
2021
* implemented or registered.
2122
*/
2223
@ReactModule(name = ReactUnimplementedViewManager.REACT_CLASS)
23-
internal class ReactUnimplementedViewManager :
24+
internal class ReactUnimplementedViewManager(
25+
private val onError: ((message: String) -> Unit) = { message: String ->
26+
FLog.e(REACT_CLASS, message)
27+
}
28+
) :
2429
ViewGroupManager<ReactUnimplementedView>(),
2530
UnimplementedNativeViewManagerInterface<ReactUnimplementedView> {
2631

@@ -30,7 +35,7 @@ internal class ReactUnimplementedViewManager :
3035
public override fun getDelegate(): ViewManagerDelegate<ReactUnimplementedView> = delegate
3136

3237
override fun createViewInstance(reactContext: ThemedReactContext): ReactUnimplementedView =
33-
ReactUnimplementedView(reactContext)
38+
ReactUnimplementedView(reactContext, onError)
3439

3540
override fun getName(): String = REACT_CLASS
3641

0 commit comments

Comments
 (0)