Compare commits
2 Commits
pr34579
...
top-setnat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5e5f2a350 | ||
|
|
ef52e22e89 |
@@ -32,6 +32,7 @@ import NativeMethodsMixin from './NativeMethodsMixin';
|
|||||||
import ReactNativeComponent from './ReactNativeComponent';
|
import ReactNativeComponent from './ReactNativeComponent';
|
||||||
import {getClosestInstanceFromNode} from './ReactFabricComponentTree';
|
import {getClosestInstanceFromNode} from './ReactFabricComponentTree';
|
||||||
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
||||||
|
import {setNativeProps} from './ReactNativeSetNativeProps';
|
||||||
|
|
||||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||||
import getComponentName from 'shared/getComponentName';
|
import getComponentName from 'shared/getComponentName';
|
||||||
@@ -104,6 +105,8 @@ const ReactFabric: ReactFabricType = {
|
|||||||
|
|
||||||
findNodeHandle,
|
findNodeHandle,
|
||||||
|
|
||||||
|
setNativeProps,
|
||||||
|
|
||||||
render(element: React$Element<any>, containerTag: any, callback: ?Function) {
|
render(element: React$Element<any>, containerTag: any, callback: ?Function) {
|
||||||
let root = roots.get(containerTag);
|
let root = roots.get(containerTag);
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import NativeMethodsMixin from './NativeMethodsMixin';
|
|||||||
import ReactNativeComponent from './ReactNativeComponent';
|
import ReactNativeComponent from './ReactNativeComponent';
|
||||||
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
|
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
|
||||||
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
||||||
|
import {setNativeProps} from './ReactNativeSetNativeProps';
|
||||||
|
|
||||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||||
import getComponentName from 'shared/getComponentName';
|
import getComponentName from 'shared/getComponentName';
|
||||||
@@ -116,6 +117,8 @@ const ReactNativeRenderer: ReactNativeType = {
|
|||||||
|
|
||||||
findNodeHandle,
|
findNodeHandle,
|
||||||
|
|
||||||
|
setNativeProps,
|
||||||
|
|
||||||
render(element: React$Element<any>, containerTag: any, callback: ?Function) {
|
render(element: React$Element<any>, containerTag: any, callback: ?Function) {
|
||||||
let root = roots.get(containerTag);
|
let root = roots.get(containerTag);
|
||||||
|
|
||||||
|
|||||||
47
packages/react-native-renderer/src/ReactNativeSetNativeProps.js
vendored
Normal file
47
packages/react-native-renderer/src/ReactNativeSetNativeProps.js
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {create} from './ReactNativeAttributePayload';
|
||||||
|
import {warnForStyleProps} from './NativeMethodsMixinUtils';
|
||||||
|
|
||||||
|
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||||
|
import getComponentName from 'shared/getComponentName';
|
||||||
|
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||||
|
|
||||||
|
// Module provided by RN:
|
||||||
|
import UIManager from 'UIManager';
|
||||||
|
|
||||||
|
const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||||||
|
|
||||||
|
export function setNativeProps(handle: any, nativeProps: Object) {
|
||||||
|
if (handle._nativeTag == null) {
|
||||||
|
warningWithoutStack(
|
||||||
|
handle._nativeTag != null,
|
||||||
|
"setNativeProps was called on a ref that isn't a " +
|
||||||
|
'native component. Use React.forwardRef to get access to the underlying native component',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__DEV__) {
|
||||||
|
warnForStyleProps(nativeProps, handle.viewConfig.validAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatePayload = create(nativeProps, handle.viewConfig.validAttributes);
|
||||||
|
// Avoid the overhead of bridge calls if there's no update.
|
||||||
|
// This is an expensive no-op for Android, and causes an unnecessary
|
||||||
|
// view invalidation for certain components (eg RCTTextInput) on iOS.
|
||||||
|
if (updatePayload != null) {
|
||||||
|
UIManager.updateView(
|
||||||
|
handle._nativeTag,
|
||||||
|
handle.viewConfig.uiViewClassName,
|
||||||
|
updatePayload,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -167,7 +167,7 @@ describe('ReactFabric', () => {
|
|||||||
expect(FabricUIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot();
|
expect(FabricUIManager.__dumpHierarchyForJestTestsOnly()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not call UIManager.updateView from setNativeProps for properties that have not changed', () => {
|
it('should not call UIManager.updateView from ref.setNativeProps for properties that have not changed', () => {
|
||||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||||
validAttributes: {foo: true},
|
validAttributes: {foo: true},
|
||||||
uiViewClassName: 'RCTView',
|
uiViewClassName: 'RCTView',
|
||||||
@@ -214,6 +214,90 @@ describe('ReactFabric', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to setNativeProps on native refs', () => {
|
||||||
|
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||||
|
validAttributes: {foo: true},
|
||||||
|
uiViewClassName: 'RCTView',
|
||||||
|
}));
|
||||||
|
|
||||||
|
UIManager.updateView.mockReset();
|
||||||
|
|
||||||
|
let viewRef;
|
||||||
|
ReactFabric.render(
|
||||||
|
<View
|
||||||
|
foo="bar"
|
||||||
|
ref={ref => {
|
||||||
|
viewRef = ref;
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(UIManager.updateView).not.toBeCalled();
|
||||||
|
ReactFabric.setNativeProps(viewRef, {foo: 'baz'});
|
||||||
|
expect(UIManager.updateView).toHaveBeenCalledTimes(1);
|
||||||
|
expect(UIManager.updateView).toHaveBeenCalledWith(
|
||||||
|
expect.any(Number),
|
||||||
|
'RCTView',
|
||||||
|
{foo: 'baz'},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should warn and no-op if calling setNativeProps on non native refs', () => {
|
||||||
|
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||||
|
validAttributes: {foo: true},
|
||||||
|
uiViewClassName: 'RCTView',
|
||||||
|
}));
|
||||||
|
|
||||||
|
class BasicClass extends React.Component {
|
||||||
|
render() {
|
||||||
|
return <React.Fragment />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Subclass extends ReactFabric.NativeComponent {
|
||||||
|
render() {
|
||||||
|
return <View />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateClass = createReactClass({
|
||||||
|
mixins: [NativeMethodsMixin],
|
||||||
|
render: () => {
|
||||||
|
return <View />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
[BasicClass, Subclass, CreateClass].forEach(Component => {
|
||||||
|
UIManager.updateView.mockReset();
|
||||||
|
|
||||||
|
let viewRef;
|
||||||
|
ReactFabric.render(
|
||||||
|
<Component
|
||||||
|
foo="bar"
|
||||||
|
ref={ref => {
|
||||||
|
viewRef = ref;
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(UIManager.updateView).not.toBeCalled();
|
||||||
|
expect(() => {
|
||||||
|
ReactFabric.setNativeProps(viewRef, {foo: 'baz'});
|
||||||
|
}).toWarnDev(
|
||||||
|
[
|
||||||
|
"Warning: setNativeProps was called on a ref that isn't a " +
|
||||||
|
'native component. Use React.forwardRef to get access ' +
|
||||||
|
'to the underlying native component',
|
||||||
|
],
|
||||||
|
{withoutStack: true},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(UIManager.updateView).not.toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('returns the correct instance and calls it in the callback', () => {
|
it('returns the correct instance and calls it in the callback', () => {
|
||||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||||
validAttributes: {foo: true},
|
validAttributes: {foo: true},
|
||||||
|
|||||||
@@ -13,12 +13,14 @@
|
|||||||
let React;
|
let React;
|
||||||
let ReactFabric;
|
let ReactFabric;
|
||||||
let ReactNative;
|
let ReactNative;
|
||||||
|
let UIManager;
|
||||||
let createReactNativeComponentClass;
|
let createReactNativeComponentClass;
|
||||||
|
|
||||||
describe('ReactFabric', () => {
|
describe('ReactFabric', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
ReactNative = require('react-native-renderer');
|
ReactNative = require('react-native-renderer');
|
||||||
|
UIManager = require('UIManager');
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
jest.mock('shared/ReactFeatureFlags', () =>
|
jest.mock('shared/ReactFeatureFlags', () =>
|
||||||
require('shared/forks/ReactFeatureFlags.native-oss'),
|
require('shared/forks/ReactFeatureFlags.native-oss'),
|
||||||
@@ -49,4 +51,25 @@ describe('ReactFabric', () => {
|
|||||||
let handle = ReactNative.findNodeHandle(ref.current);
|
let handle = ReactNative.findNodeHandle(ref.current);
|
||||||
expect(handle).toBe(2);
|
expect(handle).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets native props with setNativeProps on Fabric nodes with the RN renderer', () => {
|
||||||
|
UIManager.updateView.mockReset();
|
||||||
|
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||||
|
validAttributes: {title: true},
|
||||||
|
uiViewClassName: 'RCTView',
|
||||||
|
}));
|
||||||
|
|
||||||
|
let ref = React.createRef();
|
||||||
|
|
||||||
|
ReactFabric.render(<View title="bar" ref={ref} />, 11);
|
||||||
|
expect(UIManager.updateView).not.toBeCalled();
|
||||||
|
ReactNative.setNativeProps(ref.current, {title: 'baz'});
|
||||||
|
expect(UIManager.updateView).toHaveBeenCalledTimes(1);
|
||||||
|
expect(UIManager.updateView).toHaveBeenCalledWith(
|
||||||
|
expect.any(Number),
|
||||||
|
'RCTView',
|
||||||
|
{title: 'baz'},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -145,6 +145,90 @@ describe('ReactNative', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to setNativeProps on native refs', () => {
|
||||||
|
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||||
|
validAttributes: {foo: true},
|
||||||
|
uiViewClassName: 'RCTView',
|
||||||
|
}));
|
||||||
|
|
||||||
|
UIManager.updateView.mockReset();
|
||||||
|
|
||||||
|
let viewRef;
|
||||||
|
ReactNative.render(
|
||||||
|
<View
|
||||||
|
foo="bar"
|
||||||
|
ref={ref => {
|
||||||
|
viewRef = ref;
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(UIManager.updateView).not.toBeCalled();
|
||||||
|
ReactNative.setNativeProps(viewRef, {foo: 'baz'});
|
||||||
|
expect(UIManager.updateView).toHaveBeenCalledTimes(1);
|
||||||
|
expect(UIManager.updateView).toHaveBeenCalledWith(
|
||||||
|
expect.any(Number),
|
||||||
|
'RCTView',
|
||||||
|
{foo: 'baz'},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should warn and no-op if calling setNativeProps on non native refs', () => {
|
||||||
|
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||||
|
validAttributes: {foo: true},
|
||||||
|
uiViewClassName: 'RCTView',
|
||||||
|
}));
|
||||||
|
|
||||||
|
class BasicClass extends React.Component {
|
||||||
|
render() {
|
||||||
|
return <React.Fragment />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Subclass extends ReactNative.NativeComponent {
|
||||||
|
render() {
|
||||||
|
return <View />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateClass = createReactClass({
|
||||||
|
mixins: [NativeMethodsMixin],
|
||||||
|
render: () => {
|
||||||
|
return <View />;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
[BasicClass, Subclass, CreateClass].forEach(Component => {
|
||||||
|
UIManager.updateView.mockReset();
|
||||||
|
|
||||||
|
let viewRef;
|
||||||
|
ReactNative.render(
|
||||||
|
<Component
|
||||||
|
foo="bar"
|
||||||
|
ref={ref => {
|
||||||
|
viewRef = ref;
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
11,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(UIManager.updateView).not.toBeCalled();
|
||||||
|
expect(() => {
|
||||||
|
ReactNative.setNativeProps(viewRef, {foo: 'baz'});
|
||||||
|
}).toWarnDev(
|
||||||
|
[
|
||||||
|
"Warning: setNativeProps was called on a ref that isn't a " +
|
||||||
|
'native component. Use React.forwardRef to get access ' +
|
||||||
|
'to the underlying native component',
|
||||||
|
],
|
||||||
|
{withoutStack: true},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(UIManager.updateView).not.toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('returns the correct instance and calls it in the callback', () => {
|
it('returns the correct instance and calls it in the callback', () => {
|
||||||
const View = createReactNativeComponentClass('RCTView', () => ({
|
const View = createReactNativeComponentClass('RCTView', () => ({
|
||||||
validAttributes: {foo: true},
|
validAttributes: {foo: true},
|
||||||
|
|||||||
Reference in New Issue
Block a user