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 ReactNative = require('ReactNative');
const StyleSheet = require('StyleSheet'); const StyleSheet = require('StyleSheet');
const Text = require('Text'); const Text = require('Text');
const TextAncestor = require('TextAncestor');
const TextInputState = require('TextInputState'); const TextInputState = require('TextInputState');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error /* $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 * 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 TouchableWithoutFeedback = require('TouchableWithoutFeedback');
const UIManager = require('UIManager'); const UIManager = require('UIManager');
const ViewPropTypes = require('ViewPropTypes'); const ViewPropTypes = require('ViewPropTypes');
const {ViewContextTypes} = require('ViewContext');
const emptyFunction = require('fbjs/lib/emptyFunction'); const emptyFunction = require('fbjs/lib/emptyFunction');
const invariant = require('fbjs/lib/invariant'); const invariant = require('fbjs/lib/invariant');
@ -47,8 +47,6 @@ const onlyMultiline = {
children: true, children: true,
}; };
import type {ViewChildContext} from 'ViewContext';
if (Platform.OS === 'android') { if (Platform.OS === 'android') {
AndroidTextInput = requireNativeComponent('AndroidTextInput', null); AndroidTextInput = requireNativeComponent('AndroidTextInput', null);
} else if (Platform.OS === 'ios') { } else if (Platform.OS === 'ios') {
@ -701,16 +699,7 @@ const TextInput = createReactClass({
} }
}, },
getChildContext(): ViewChildContext {
return {
isInAParentText: true,
};
},
childContextTypes: ViewContextTypes,
contextTypes: { contextTypes: {
...ViewContextTypes,
onFocusRequested: PropTypes.func, onFocusRequested: PropTypes.func,
focusEmitter: PropTypes.instanceOf(EventEmitter), focusEmitter: PropTypes.instanceOf(EventEmitter),
}, },
@ -723,13 +712,17 @@ const TextInput = createReactClass({
}, },
render: function() { render: function() {
let textInput;
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
return UIManager.RCTVirtualText textInput = UIManager.RCTVirtualText
? this._renderIOS() ? this._renderIOS()
: this._renderIOSLegacy(); : this._renderIOSLegacy();
} else if (Platform.OS === 'android') { } else if (Platform.OS === 'android') {
return this._renderAndroid(); textInput = this._renderAndroid();
} }
return (
<TextAncestor.Provider value={true}>{textInput}</TextAncestor.Provider>
);
}, },
_getText: function(): ?string { _getText: function(): ?string {

View File

@ -7,6 +7,7 @@
* @flow * @flow
* @format * @format
*/ */
'use strict'; 'use strict';
const Platform = require('Platform'); const Platform = require('Platform');
@ -14,13 +15,13 @@ const React = require('React');
const ReactNative = require('ReactNative'); const ReactNative = require('ReactNative');
const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes'); const ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
const ReactNativeViewAttributes = require('ReactNativeViewAttributes'); const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
const TextAncestor = require('TextAncestor');
const ViewPropTypes = require('ViewPropTypes'); const ViewPropTypes = require('ViewPropTypes');
const {ViewContextTypes} = require('ViewContext');
const invariant = require('fbjs/lib/invariant'); const invariant = require('fbjs/lib/invariant');
const requireNativeComponent = require('requireNativeComponent'); const requireNativeComponent = require('requireNativeComponent');
import type {ViewProps} from 'ViewPropTypes'; import type {ViewProps} from 'ViewPropTypes';
import type {ViewChildContext} from 'ViewContext';
export type Props = ViewProps; export type Props = ViewProps;
@ -33,30 +34,31 @@ export type Props = ViewProps;
*/ */
class View extends ReactNative.NativeComponent<Props> { class View extends ReactNative.NativeComponent<Props> {
static propTypes = ViewPropTypes; static propTypes = ViewPropTypes;
static childContextTypes = ViewContextTypes;
viewConfig = { viewConfig = {
uiViewClassName: 'RCTView', uiViewClassName: 'RCTView',
validAttributes: ReactNativeViewAttributes.RCTView, validAttributes: ReactNativeViewAttributes.RCTView,
}; };
getChildContext(): ViewChildContext { /**
return { * WARNING: This method will not be used in production mode as in that mode we
isInAParentText: false, * 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() { render() {
invariant( return (
!(this.context.isInAParentText && Platform.OS === 'android'), <TextAncestor.Consumer>
'Nesting of <View> within <Text> is not supported on Android.', {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 * @flow
* @format * @format
*/ */
'use strict'; 'use strict';
var ImageResizeMode = require('ImageResizeMode'); var ImageResizeMode = require('ImageResizeMode');
@ -18,6 +19,7 @@ var PropTypes = require('prop-types');
var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes');
var StyleSheet = require('StyleSheet'); var StyleSheet = require('StyleSheet');
var StyleSheetPropType = require('StyleSheetPropType'); var StyleSheetPropType = require('StyleSheetPropType');
const TextAncestor = require('TextAncestor');
var ViewPropTypes = require('ViewPropTypes'); var ViewPropTypes = require('ViewPropTypes');
var createReactClass = require('create-react-class'); var createReactClass = require('create-react-class');
@ -26,8 +28,6 @@ var merge = require('merge');
var requireNativeComponent = require('requireNativeComponent'); var requireNativeComponent = require('requireNativeComponent');
var resolveAssetSource = require('resolveAssetSource'); var resolveAssetSource = require('resolveAssetSource');
const {ViewContextTypes} = require('ViewContext');
var {ImageLoader} = NativeModules; var {ImageLoader} = NativeModules;
let _requestId = 1; let _requestId = 1;
@ -202,8 +202,6 @@ var Image = createReactClass({
validAttributes: ReactNativeViewAttributes.RCTView, validAttributes: ReactNativeViewAttributes.RCTView,
}, },
contextTypes: ViewContextTypes,
render: function() { render: function() {
const source = resolveAssetSource(this.props.source); const source = resolveAssetSource(this.props.source);
const defaultSource = resolveAssetSource(this.props.defaultSource); const defaultSource = resolveAssetSource(this.props.defaultSource);
@ -236,42 +234,44 @@ var Image = createReactClass({
); );
} }
if (source && (source.uri || Array.isArray(source))) { if (!source || (!source.uri && !Array.isArray(source))) {
let style; return null;
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} />;
}
} }
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 React = require('React');
const ReactNative = require('ReactNative'); const ReactNative = require('ReactNative');
const ReactNativeViewAttributes = require('ReactNativeViewAttributes'); const ReactNativeViewAttributes = require('ReactNativeViewAttributes');
const TextAncestor = require('TextAncestor');
const TextPropTypes = require('TextPropTypes'); const TextPropTypes = require('TextPropTypes');
const Touchable = require('Touchable'); const Touchable = require('Touchable');
const UIManager = require('UIManager'); const UIManager = require('UIManager');
@ -19,11 +20,9 @@ const UIManager = require('UIManager');
const createReactNativeComponentClass = require('createReactNativeComponentClass'); const createReactNativeComponentClass = require('createReactNativeComponentClass');
const mergeFast = require('mergeFast'); const mergeFast = require('mergeFast');
const processColor = require('processColor'); const processColor = require('processColor');
const {ViewContextTypes} = require('ViewContext');
import type {PressEvent} from 'CoreEventTypes'; import type {PressEvent} from 'CoreEventTypes';
import type {TextProps} from 'TextProps'; import type {TextProps} from 'TextProps';
import type {ViewChildContext} from 'ViewContext';
type State = { type State = {
isHighlighted: boolean, isHighlighted: boolean,
@ -61,8 +60,6 @@ const viewConfig = {
*/ */
class Text extends ReactNative.NativeComponent<TextProps, State> { class Text extends ReactNative.NativeComponent<TextProps, State> {
static propTypes = TextPropTypes; static propTypes = TextPropTypes;
static childContextTypes = ViewContextTypes;
static contextTypes = ViewContextTypes;
static defaultProps = { static defaultProps = {
accessible: true, accessible: true,
@ -76,12 +73,6 @@ class Text extends ReactNative.NativeComponent<TextProps, State> {
viewConfig = viewConfig; viewConfig = viewConfig;
getChildContext(): ViewChildContext {
return {
isInAParentText: true,
};
}
_handlers: ?Object; _handlers: ?Object;
_hasPressHandler(): boolean { _hasPressHandler(): boolean {
@ -215,11 +206,19 @@ class Text extends ReactNative.NativeComponent<TextProps, State> {
style: [this.props.style, {color: 'magenta'}], style: [this.props.style, {color: 'magenta'}],
}; };
} }
if (this.context.isInAParentText) { return (
return <RCTVirtualText {...newProps} />; <TextAncestor.Consumer>
} else { {hasTextAncestor =>
return <RCTText {...newProps} />; hasTextAncestor ? (
} <RCTVirtualText {...newProps} />
) : (
<TextAncestor.Provider value={true}>
<RCTText {...newProps} />
</TextAncestor.Provider>
)
}
</TextAncestor.Consumer>
);
} }
} }

View File

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