RN: Replace `context.isInAParentText` w/ React.createContext

Reviewed By: sahrens

Differential Revision: D7895382

fbshipit-source-id: 4affcecd147b8e8c506e0d94f223bac3e6dfdf66
This commit is contained in:
Tim Yung 2018-05-09 00:47:46 -07:00 committed by Facebook Github Bot
parent 5d4c542c58
commit e1339bc183
5 changed files with 86 additions and 96 deletions

View File

@ -20,6 +20,7 @@ const PropTypes = require('prop-types');
const ReactNative = require('ReactNative');
const StyleSheet = require('StyleSheet');
const Text = require('Text');
const TextAncestor = require('TextAncestor');
const TextInputState = require('TextInputState');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
* found when Flow v0.54 was deployed. To see the error delete this comment and
@ -28,7 +29,6 @@ const TimerMixin = require('react-timer-mixin');
const TouchableWithoutFeedback = require('TouchableWithoutFeedback');
const UIManager = require('UIManager');
const ViewPropTypes = require('ViewPropTypes');
const {ViewContextTypes} = require('ViewContext');
const emptyFunction = require('fbjs/lib/emptyFunction');
const invariant = require('fbjs/lib/invariant');
@ -47,8 +47,6 @@ const onlyMultiline = {
children: true,
};
import type {ViewChildContext} from 'ViewContext';
if (Platform.OS === 'android') {
AndroidTextInput = requireNativeComponent('AndroidTextInput', null);
} else if (Platform.OS === 'ios') {
@ -701,16 +699,7 @@ const TextInput = createReactClass({
}
},
getChildContext(): ViewChildContext {
return {
isInAParentText: true,
};
},
childContextTypes: ViewContextTypes,
contextTypes: {
...ViewContextTypes,
onFocusRequested: PropTypes.func,
focusEmitter: PropTypes.instanceOf(EventEmitter),
},
@ -723,13 +712,17 @@ const TextInput = createReactClass({
},
render: function() {
let textInput;
if (Platform.OS === 'ios') {
return UIManager.RCTVirtualText
textInput = UIManager.RCTVirtualText
? this._renderIOS()
: this._renderIOSLegacy();
} else if (Platform.OS === 'android') {
return this._renderAndroid();
textInput = this._renderAndroid();
}
return (
<TextAncestor.Provider value={true}>{textInput}</TextAncestor.Provider>
);
},
_getText: function(): ?string {

View File

@ -7,6 +7,7 @@
* @flow
* @format
*/
'use strict';
const Platform = require('Platform');
@ -14,13 +15,13 @@ 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 {ViewContextTypes} = require('ViewContext');
const invariant = require('fbjs/lib/invariant');
const requireNativeComponent = require('requireNativeComponent');
import type {ViewProps} from 'ViewPropTypes';
import type {ViewChildContext} from 'ViewContext';
export type Props = ViewProps;
@ -33,30 +34,31 @@ export type Props = ViewProps;
*/
class View extends ReactNative.NativeComponent<Props> {
static propTypes = ViewPropTypes;
static childContextTypes = ViewContextTypes;
viewConfig = {
uiViewClassName: 'RCTView',
validAttributes: ReactNativeViewAttributes.RCTView,
};
getChildContext(): ViewChildContext {
return {
isInAParentText: false,
};
}
/**
* 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() {
invariant(
!(this.context.isInAParentText && Platform.OS === 'android'),
'Nesting of <View> within <Text> is not supported on Android.',
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>
);
// 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.
return <RCTView {...this.props} />;
}
}

View File

@ -7,6 +7,7 @@
* @flow
* @format
*/
'use strict';
var ImageResizeMode = require('ImageResizeMode');
@ -18,6 +19,7 @@ var PropTypes = require('prop-types');
var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheet = require('StyleSheet');
var StyleSheetPropType = require('StyleSheetPropType');
const TextAncestor = require('TextAncestor');
var ViewPropTypes = require('ViewPropTypes');
var createReactClass = require('create-react-class');
@ -26,8 +28,6 @@ var merge = require('merge');
var requireNativeComponent = require('requireNativeComponent');
var resolveAssetSource = require('resolveAssetSource');
const {ViewContextTypes} = require('ViewContext');
var {ImageLoader} = NativeModules;
let _requestId = 1;
@ -202,8 +202,6 @@ var Image = createReactClass({
validAttributes: ReactNativeViewAttributes.RCTView,
},
contextTypes: ViewContextTypes,
render: function() {
const source = resolveAssetSource(this.props.source);
const defaultSource = resolveAssetSource(this.props.defaultSource);
@ -236,42 +234,44 @@ var Image = createReactClass({
);
}
if (source && (source.uri || Array.isArray(source))) {
let style;
let sources;
if (source.uri) {
const {width, height} = source;
style = flattenStyle([{width, height}, styles.base, this.props.style]);
sources = [{uri: source.uri}];
} else {
style = flattenStyle([styles.base, this.props.style]);
sources = source;
}
const {onLoadStart, onLoad, onLoadEnd, onError} = this.props;
const nativeProps = merge(this.props, {
style,
shouldNotifyLoadEvents: !!(
onLoadStart ||
onLoad ||
onLoadEnd ||
onError
),
src: sources,
headers: source.headers,
defaultSrc: defaultSource ? defaultSource.uri : null,
loadingIndicatorSrc: loadingIndicatorSource
? loadingIndicatorSource.uri
: null,
});
if (this.context.isInAParentText) {
return <RCTTextInlineImage {...nativeProps} />;
} else {
return <RKImage {...nativeProps} />;
}
if (!source || (!source.uri && !Array.isArray(source))) {
return null;
}
return null;
let style;
let sources;
if (source.uri) {
const {width, height} = source;
style = flattenStyle([{width, height}, styles.base, this.props.style]);
sources = [{uri: source.uri}];
} else {
style = flattenStyle([styles.base, this.props.style]);
sources = source;
}
const {onLoadStart, onLoad, onLoadEnd, onError} = this.props;
const nativeProps = merge(this.props, {
style,
shouldNotifyLoadEvents: !!(onLoadStart || onLoad || onLoadEnd || onError),
src: sources,
headers: source.headers,
defaultSrc: defaultSource ? defaultSource.uri : null,
loadingIndicatorSrc: loadingIndicatorSource
? loadingIndicatorSource.uri
: null,
});
return (
<TextAncestor.Consumer>
{hasTextAncestor =>
hasTextAncestor ? (
<RCTTextInlineImage {...nativeProps} />
) : (
<RKImage {...nativeProps} />
)
}
</TextAncestor.Consumer>
);
},
});

View File

@ -12,6 +12,7 @@
const React = require('React');
const ReactNative = require('ReactNative');
const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
const TextAncestor = require('TextAncestor');
const TextPropTypes = require('TextPropTypes');
const Touchable = require('Touchable');
const UIManager = require('UIManager');
@ -19,11 +20,9 @@ const UIManager = require('UIManager');
const createReactNativeComponentClass = require('createReactNativeComponentClass');
const mergeFast = require('mergeFast');
const processColor = require('processColor');
const {ViewContextTypes} = require('ViewContext');
import type {PressEvent} from 'CoreEventTypes';
import type {TextProps} from 'TextProps';
import type {ViewChildContext} from 'ViewContext';
type State = {
isHighlighted: boolean,
@ -61,8 +60,6 @@ const viewConfig = {
*/
class Text extends ReactNative.NativeComponent<TextProps, State> {
static propTypes = TextPropTypes;
static childContextTypes = ViewContextTypes;
static contextTypes = ViewContextTypes;
static defaultProps = {
accessible: true,
@ -76,12 +73,6 @@ class Text extends ReactNative.NativeComponent<TextProps, State> {
viewConfig = viewConfig;
getChildContext(): ViewChildContext {
return {
isInAParentText: true,
};
}
_handlers: ?Object;
_hasPressHandler(): boolean {
@ -215,11 +206,19 @@ class Text extends ReactNative.NativeComponent<TextProps, State> {
style: [this.props.style, {color: 'magenta'}],
};
}
if (this.context.isInAParentText) {
return <RCTVirtualText {...newProps} />;
} else {
return <RCTText {...newProps} />;
}
return (
<TextAncestor.Consumer>
{hasTextAncestor =>
hasTextAncestor ? (
<RCTVirtualText {...newProps} />
) : (
<TextAncestor.Provider value={true}>
<RCTText {...newProps} />
</TextAncestor.Provider>
)
}
</TextAncestor.Consumer>
);
}
}

View File

@ -7,16 +7,12 @@
* @flow
* @format
*/
'use strict';
const PropTypes = require('prop-types');
const React = require('React');
export type ViewChildContext = {|
+isInAParentText: boolean,
|};
module.exports = {
ViewContextTypes: {
isInAParentText: PropTypes.bool,
},
};
/**
* Whether the current element is the descendant of a <Text> element.
*/
module.exports = React.createContext(false);