Repository: facebook/react
Pull request: Add support for setNativeProps to Fabric

Add support for `setNativeProps` in Fabric to make migration to the new architecture easier. The React Native part of this has already landed in the core and iOS in https://github.com/facebook/react-native/commit/1d3fa40c59b234f21f516db85c322ec0ed0311e0.

It is still recommended to move away from `setNativeProps` because the API will not work with future features.


Changed files:
filename: packages/react-native-renderer/src/ReactFabricHostConfig.js
patch: @@ -18,7 +18,10 @@ import type {
   TouchedViewDataAtPoint,
 } from './ReactNativeTypes';
 
-import {mountSafeCallback_NOT_REALLY_SAFE} from './NativeMethodsMixinUtils';
+import {
+  mountSafeCallback_NOT_REALLY_SAFE,
+  warnForStyleProps,
+} from './NativeMethodsMixinUtils';
 import {create, diff} from './ReactNativeAttributePayload';
 
 import {dispatchEvent} from './ReactFabricEventEmitter';
@@ -52,6 +55,7 @@ const {
   unstable_DefaultEventPriority: FabricDefaultPriority,
   unstable_DiscreteEventPriority: FabricDiscretePriority,
   unstable_getCurrentEventPriority: fabricGetCurrentEventPriority,
+  setNativeProps,
 } = nativeFabricUIManager;
 
 const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
@@ -208,12 +212,14 @@ class ReactFabricHostComponent {
 
   setNativeProps(nativeProps: Object) {
     if (__DEV__) {
-      console.error(
-        'Warning: setNativeProps is not currently supported in Fabric',
-      );
+      warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
     }
+    const updatePayload = create(nativeProps, this.viewConfig.validAttributes);
 
-    return;
+    const {stateNode} = this._internalInstanceHandle;
+    if (stateNode != null && updatePayload != null) {
+      setNativeProps(stateNode.node, updatePayload);
+    }
   }
 
   // This API (addEventListener, removeEventListener) attempts to adhere to the

filename: packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/InitializeNativeFabricUIManager.js
patch: @@ -117,6 +117,8 @@ const RCTFabricUIManager = {
 
   dispatchCommand: jest.fn(),
 
+  setNativeProps: jest.fn(),
+
   sendAccessibilityEvent: jest.fn(),
 
   registerEventHandler: jest.fn(function registerEventHandler(callback) {}),

filename: packages/react-native-renderer/src/__tests__/ReactFabricHostComponent-test.internal.js
patch: @@ -38,7 +38,7 @@ function mockRenderKeys(keyLists) {
 
   const mockContainerTag = 11;
   const MockView = createReactNativeComponentClass('RCTMockView', () => ({
-    validAttributes: {},
+    validAttributes: {foo: true},
     uiViewClassName: 'RCTMockView',
   }));
 
@@ -200,21 +200,15 @@ describe('measureLayout', () => {
 });
 
 describe('setNativeProps', () => {
-  test('setNativeProps(...) emits a warning', () => {
+  test('setNativeProps(...) invokes setNativeProps on Fabric UIManager', () => {
     const {
       UIManager,
     } = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface');
 
     const [[fooRef]] = mockRenderKeys([['foo']]);
+    fooRef.setNativeProps({foo: 'baz'});
 
-    expect(() => {
-      fooRef.setNativeProps({});
-    }).toErrorDev(
-      ['Warning: setNativeProps is not currently supported in Fabric'],
-      {
-        withoutStack: true,
-      },
-    );
     expect(UIManager.updateView).not.toBeCalled();
+    expect(nativeFabricUIManager.setNativeProps).toHaveBeenCalledTimes(1);
   });
 });

filename: scripts/flow/react-native-host-hooks.js
patch: @@ -186,7 +186,7 @@ declare var nativeFabricUIManager: {
       payload: Object,
     ) => void,
   ) => void,
-
+  setNativeProps: (node: Object, nativeProps: Object) => Object,
   dispatchCommand: (node: Object, command: string, args: Array<any>) => void,
   sendAccessibilityEvent: (node: Object, eventTypeName: string) => void,