RN: Switch `View` to `React.forwardRef`

Reviewed By: bvaughn, sophiebits

Differential Revision: D7896711

fbshipit-source-id: c10c8a14a00ac2d67605e6e4fe1a341b4688fdd8
This commit is contained in:
Tim Yung 2018-05-09 00:47:48 -07:00 committed by Facebook Github Bot
parent e1339bc183
commit 3e534b9aab
4 changed files with 45 additions and 48 deletions

View File

@ -18,7 +18,7 @@ const invariant = require('fbjs/lib/invariant');
function createAnimatedComponent(Component: any): any {
invariant(
typeof Component === 'string' ||
typeof Component !== 'function' ||
(Component.prototype && Component.prototype.isReactComponent),
'`createAnimatedComponent` does not support stateless functional components; ' +
'use a class component instead.',

View File

@ -4,23 +4,22 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @flow
*/
'use strict';
const Platform = require('Platform');
const React = require('React');
const ReactNative = require('ReactNative');
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
const TextAncestor = require('TextAncestor');
const ViewPropTypes = require('ViewPropTypes');
const invariant = require('fbjs/lib/invariant');
const requireNativeComponent = require('requireNativeComponent');
import type {NativeComponent} from 'ReactNative';
import type {ViewProps} from 'ViewPropTypes';
export type Props = ViewProps;
@ -32,50 +31,25 @@ export type Props = ViewProps;
*
* @see http://facebook.github.io/react-native/docs/view.html
*/
class View extends ReactNative.NativeComponent<Props> {
static propTypes = ViewPropTypes;
viewConfig = {
uiViewClassName: 'RCTView',
validAttributes: ReactNativeViewAttributes.RCTView,
};
/**
* WARNING: This method will not be used in production mode as in that mode we
* replace wrapper component View with generated native wrapper RCTView. Avoid
* adding functionality this component that you'd want to be available in both
* dev and prod modes.
*/
render() {
return (
<TextAncestor.Consumer>
{hasTextAncestor => {
// TODO: Change iOS to behave the same as Android.
invariant(
!hasTextAncestor || Platform.OS !== 'android',
'Nesting of <View> within <Text> is not supported on Android.',
);
return <RCTView {...this.props} />;
}}
</TextAncestor.Consumer>
);
}
}
const RCTView = requireNativeComponent('RCTView', View, {
nativeOnly: {
nativeBackgroundAndroid: true,
nativeForegroundAndroid: true,
const RCTView = requireNativeComponent(
'RCTView',
{
propTypes: ViewPropTypes,
},
});
{
nativeOnly: {
nativeBackgroundAndroid: true,
nativeForegroundAndroid: true,
},
},
);
if (__DEV__) {
const UIManager = require('UIManager');
const viewConfig =
(UIManager.viewConfigs && UIManager.viewConfigs.RCTView) || {};
for (const prop in viewConfig.nativeProps) {
const viewAny: any = View; // Appease flow
if (!viewAny.propTypes[prop] && !ReactNativeStyleAttributes[prop]) {
if (!ViewPropTypes[prop] && !ReactNativeStyleAttributes[prop]) {
throw new Error(
'View is missing propType for native prop `' + prop + '`',
);
@ -85,8 +59,20 @@ if (__DEV__) {
let ViewToExport = RCTView;
if (__DEV__) {
ViewToExport = View;
// $FlowFixMe - TODO T29156721 `React.forwardRef` is not defined in Flow, yet.
ViewToExport = React.forwardRef((props, ref) => (
<TextAncestor.Consumer>
{hasTextAncestor => {
// TODO: Change iOS to behave the same as Android.
invariant(
!hasTextAncestor || Platform.OS !== 'android',
'Nesting of <View> within <Text> is not supported on Android.',
);
return <RCTView {...props} ref={ref} />;
}}
</TextAncestor.Consumer>
));
ViewToExport.displayName = 'View';
}
// No one should depend on the DEV-mode createClass View wrapper.
module.exports = ((ViewToExport: any): typeof View);
module.exports = ((ViewToExport: any): Class<NativeComponent<ViewProps, any>>);

View File

@ -3,23 +3,34 @@
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
module.exports = moduleName => {
module.exports = (moduleName, instanceMethods) => {
const RealComponent = require.requireActual(moduleName);
const React = require('react');
const Component = class extends RealComponent {
const SuperClass =
typeof RealComponent === 'function' ? RealComponent : React.Component;
const Component = class extends SuperClass {
render() {
const name = RealComponent.displayName || RealComponent.name;
return React.createElement(
name.replace(/^(RCT|RK)/,''),
name.replace(/^(RCT|RK)/, ''),
this.props,
this.props.children,
);
}
};
if (instanceMethods != null) {
Object.assign(Component.prototype, instanceMethods);
}
return Component;
};

View File

@ -38,7 +38,7 @@ jest
.mock('Text', () => mockComponent('Text'))
.mock('TextInput', () => mockComponent('TextInput'))
.mock('Modal', () => mockComponent('Modal'))
.mock('View', () => mockComponent('View'))
.mock('View', () => mockComponent('View', MockNativeMethods))
.mock('RefreshControl', () => require.requireMock('RefreshControlMock'))
.mock('ScrollView', () => require.requireMock('ScrollViewMock'))
.mock(