From f922b741b767791698f104163f4d566f34a2e782 Mon Sep 17 00:00:00 2001 From: Aswin Andro Date: Sat, 21 Feb 2026 14:16:36 +0400 Subject: [PATCH] #55350-RuntimeException: Unrecognized type: class java.lang.Float for method: SomeViewManager#setProp Introduce BoxedFloatPropSetter in ViewManagersPropertyCache to support boxed Float props (converts incoming Double values to float and preserves null). Update factory logic to return the new setter for Float.class and handle ReactPropGroup cases. Add tests in ReactPropAnnotationSetterTest for boxed float prop and grouped props, and update ReactPropConstantsTest to include boxed float mappings to "number". --- .../uimanager/ViewManagersPropertyCache.java | 29 ++++++++++++ .../ReactPropAnnotationSetterTest.kt | 46 +++++++++++++++++++ .../react/uimanager/ReactPropConstantsTest.kt | 8 ++++ 3 files changed, 83 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java index 09ae7a272ad0..7b98cae55d99 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagersPropertyCache.java @@ -330,6 +330,29 @@ public BoxedIntPropSetter(ReactPropGroup prop, Method setter, int index) { } } + private static class BoxedFloatPropSetter extends PropSetter { + + public BoxedFloatPropSetter(ReactProp prop, Method setter) { + super(prop, "number", setter); + } + + public BoxedFloatPropSetter(ReactPropGroup prop, Method setter, int index) { + super(prop, "number", setter, index); + } + + @Override + protected @Nullable Object getValueOrDefault(Object value, Context context) { + if (value != null) { + if (value instanceof Double) { + return ((Double) value).floatValue(); + } else { + return (Float) value; + } + } + return null; + } + } + private static class BoxedColorPropSetter extends PropSetter { public BoxedColorPropSetter(ReactProp prop, Method setter) { @@ -450,6 +473,8 @@ private static PropSetter createPropSetter( return new BoxedColorPropSetter(annotation, method); } return new BoxedIntPropSetter(annotation, method); + } else if (propTypeClass == Float.class) { + return new BoxedFloatPropSetter(annotation, method); } else if (propTypeClass == ReadableArray.class) { return new ArrayPropSetter(annotation, method); } else if (propTypeClass == ReadableMap.class) { @@ -500,6 +525,10 @@ private static void createPropSetters( props.put(names[i], new BoxedIntPropSetter(annotation, method, i)); } } + } else if (propTypeClass == Float.class) { + for (int i = 0; i < names.length; i++) { + props.put(names[i], new BoxedFloatPropSetter(annotation, method, i)); + } } else { throw new RuntimeException( "Unrecognized type: " diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropAnnotationSetterTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropAnnotationSetterTest.kt index e2e683a7c443..23abeecccbb0 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropAnnotationSetterTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropAnnotationSetterTest.kt @@ -41,6 +41,8 @@ class ReactPropAnnotationSetterTest { fun onBoxedIntSetterCalled(value: Int?) + fun onBoxedFloatSetterCalled(value: Float?) + fun onArraySetterCalled(value: ReadableArray?) fun onMapSetterCalled(value: ReadableMap?) @@ -50,6 +52,8 @@ class ReactPropAnnotationSetterTest { fun onIntGroupPropSetterCalled(index: Int, value: Int) fun onBoxedIntGroupPropSetterCalled(index: Int, value: Int?) + + fun onBoxedFloatGroupPropSetterCalled(index: Int, value: Float?) } @Suppress("UNUSED_PARAMETER", "DEPRECATION") @@ -128,6 +132,11 @@ class ReactPropAnnotationSetterTest { viewManagerUpdatesReceiver.onBoxedIntSetterCalled(value) } + @ReactProp(name = "boxedFloatProp") + fun setBoxedFloatProp(v: View?, value: Float?) { + viewManagerUpdatesReceiver.onBoxedFloatSetterCalled(value) + } + @ReactProp(name = "arrayProp") fun setArrayProp(v: View?, value: ReadableArray?) { viewManagerUpdatesReceiver.onArraySetterCalled(value) @@ -168,6 +177,11 @@ class ReactPropAnnotationSetterTest { fun setBoxedIntGroupProp(v: View?, index: Int, value: Int?) { viewManagerUpdatesReceiver.onBoxedIntGroupPropSetterCalled(index, value) } + + @ReactPropGroup(names = ["boxedFloatGroupPropFirst", "boxedFloatGroupPropSecond"]) + fun setBoxedFloatGroupProp(v: View?, index: Int, value: Float?) { + viewManagerUpdatesReceiver.onBoxedFloatGroupPropSetterCalled(index, value) + } } private lateinit var viewManager: ViewManagerUnderTest @@ -314,6 +328,22 @@ class ReactPropAnnotationSetterTest { Mockito.reset(updatesReceiverMock) } + @Test + fun testBoxedFloatSetter() { + viewManager.updateProperties(targetView, buildStyles("boxedFloatProp", 55.5)) + Mockito.verify(updatesReceiverMock).onBoxedFloatSetterCalled(55.5f) + Mockito.verifyNoMoreInteractions(updatesReceiverMock) + Mockito.reset(updatesReceiverMock) + viewManager.updateProperties(targetView, buildStyles("boxedFloatProp", 1.0)) + Mockito.verify(updatesReceiverMock).onBoxedFloatSetterCalled(1.0f) + Mockito.verifyNoMoreInteractions(updatesReceiverMock) + Mockito.reset(updatesReceiverMock) + viewManager.updateProperties(targetView, buildStyles("boxedFloatProp", null)) + Mockito.verify(updatesReceiverMock).onBoxedFloatSetterCalled(null) + Mockito.verifyNoMoreInteractions(updatesReceiverMock) + Mockito.reset(updatesReceiverMock) + } + @Test fun testArraySetter() { val array: ReadableArray = JavaOnlyArray() @@ -404,6 +434,22 @@ class ReactPropAnnotationSetterTest { Mockito.reset(updatesReceiverMock) } + @Test + fun testBoxedFloatGroupSetter() { + viewManager.updateProperties(targetView, buildStyles("boxedFloatGroupPropFirst", -7.75)) + Mockito.verify(updatesReceiverMock).onBoxedFloatGroupPropSetterCalled(0, -7.75f) + Mockito.verifyNoMoreInteractions(updatesReceiverMock) + Mockito.reset(updatesReceiverMock) + viewManager.updateProperties(targetView, buildStyles("boxedFloatGroupPropSecond", 12345.0)) + Mockito.verify(updatesReceiverMock).onBoxedFloatGroupPropSetterCalled(1, 12345.0f) + Mockito.verifyNoMoreInteractions(updatesReceiverMock) + Mockito.reset(updatesReceiverMock) + viewManager.updateProperties(targetView, buildStyles("boxedFloatGroupPropSecond", null)) + Mockito.verify(updatesReceiverMock).onBoxedFloatGroupPropSetterCalled(1, null) + Mockito.verifyNoMoreInteractions(updatesReceiverMock) + Mockito.reset(updatesReceiverMock) + } + @Test(expected = JSApplicationIllegalArgumentException::class) fun testFailToUpdateBoolPropWithMap() { viewManager.updateProperties(targetView, buildStyles("boolProp", JavaOnlyMap())) diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt index bee7ae8b9adf..e1b11ba6de87 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.kt @@ -66,6 +66,8 @@ class ReactPropConstantsTest { @ReactProp(name = "boxedIntProp") fun setBoxedIntProp(v: View?, value: Int?) = Unit + @ReactProp(name = "boxedFloatProp") fun setBoxedFloatProp(v: View?, value: Float?) = Unit + @ReactProp(name = "arrayProp") fun setArrayProp(v: View?, value: ReadableArray?) = Unit @ReactProp(name = "mapProp") fun setMapProp(v: View?, value: ReadableMap?) = Unit @@ -79,6 +81,9 @@ class ReactPropConstantsTest { @ReactPropGroup(names = ["boxedIntGroupPropFirst", "boxedIntGroupPropSecond"]) fun setBoxedIntGroupProp(v: View?, index: Int, value: Int?) = Unit + @ReactPropGroup(names = ["boxedFloatGroupPropFirst", "boxedFloatGroupPropSecond"]) + fun setBoxedFloatGroupProp(v: View?, index: Int, value: Float?) = Unit + @ReactProp(name = "customIntProp", customType = "date") fun customIntProp(v: View?, value: Int) = Unit @@ -112,6 +117,7 @@ class ReactPropConstantsTest { "stringProp" to "String", "boxedBoolProp" to "boolean", "boxedIntProp" to "number", + "boxedFloatProp" to "number", "arrayProp" to "Array", "mapProp" to "Map", "floatGroupPropFirst" to "number", @@ -120,6 +126,8 @@ class ReactPropConstantsTest { "intGroupPropSecond" to "number", "boxedIntGroupPropFirst" to "number", "boxedIntGroupPropSecond" to "number", + "boxedFloatGroupPropFirst" to "number", + "boxedFloatGroupPropSecond" to "number", "customIntProp" to "date", "customBoxedIntGroupPropFirst" to "color", "customBoxedIntGroupPropSecond" to "color",