Skip to content

Commit

Permalink
RN: Switch View to React.forwardRef
Browse files Browse the repository at this point in the history
Reviewed By: bvaughn, sophiebits

Differential Revision: D7896711

fbshipit-source-id: c10c8a14a00ac2d67605e6e4fe1a341b4688fdd8
  • Loading branch information
yungsters authored and facebook-github-bot committed May 9, 2018
1 parent e1339bc commit 3e534b9
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Libraries/Animated/src/createAnimatedComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
Expand Down
72 changes: 29 additions & 43 deletions Libraries/Components/View/View.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 + '`',
);
Expand All @@ -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>>);
17 changes: 14 additions & 3 deletions jest/mockComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
2 changes: 1 addition & 1 deletion jest/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 3e534b9

Please sign in to comment.