diff --git a/src/apis/AppRegistry/index.js b/src/apis/AppRegistry/index.js index e6374288..0e9e9408 100644 --- a/src/apis/AppRegistry/index.js +++ b/src/apis/AppRegistry/index.js @@ -11,6 +11,7 @@ import invariant from 'fbjs/lib/invariant'; import { unmountComponentAtNode } from 'react/lib/ReactMount'; import renderApplication, { getApplication } from './renderApplication'; +const emptyObject = {}; const runnables = {}; type ComponentProvider = () => Component @@ -41,8 +42,8 @@ class AppRegistry { static registerComponent(appKey: string, getComponentFunc: ComponentProvider): string { runnables[appKey] = { - getApplication: ({ initialProps } = {}) => getApplication(getComponentFunc(), initialProps), - run: ({ initialProps, rootTag }) => renderApplication(getComponentFunc(), initialProps, rootTag) + getApplication: ({ initialProps } = emptyObject) => getApplication(getComponentFunc(), initialProps), + run: ({ initialProps = emptyObject, rootTag }) => renderApplication(getComponentFunc(), initialProps, rootTag) }; return appKey; } diff --git a/src/apis/StyleSheet/i18nStyle.js b/src/apis/StyleSheet/i18nStyle.js index e28d783d..af57184d 100644 --- a/src/apis/StyleSheet/i18nStyle.js +++ b/src/apis/StyleSheet/i18nStyle.js @@ -1,6 +1,8 @@ import I18nManager from '../I18nManager'; import multiplyStyleLengthValue from '../../modules/multiplyStyleLengthValue'; +const emptyObject = {}; + /** * Map of property names to their BiDi equivalent. */ @@ -64,38 +66,40 @@ const swapLtrRtl = (value:String): String => { return value === 'ltr' ? 'rtl' : value === 'rtl' ? 'ltr' : value; }; -const i18nStyle = (style = {}) => { +const i18nStyle = (style = emptyObject) => { const newStyle = {}; for (const prop in style) { - if (style.hasOwnProperty(prop)) { - const indexOfNoFlip = prop.indexOf('$noI18n'); + if (!Object.prototype.hasOwnProperty.call(style, prop)) { + continue; + } - if (I18nManager.isRTL) { - if (PROPERTIES_TO_SWAP[prop]) { - const newProp = flipProperty(prop); - newStyle[newProp] = style[prop]; - } else if (PROPERTIES_SWAP_LEFT_RIGHT[prop]) { - newStyle[prop] = swapLeftRight(style[prop]); - } else if (PROPERTIES_SWAP_LTR_RTL[prop]) { - newStyle[prop] = swapLtrRtl(style[prop]); - } else if (prop === 'textShadowOffset') { - newStyle[prop] = style[prop]; - newStyle[prop].width = additiveInverse(style[prop].width); - } else if (prop === 'transform') { - newStyle[prop] = style[prop].map(flipTransform); - } else if (indexOfNoFlip > -1) { - const newProp = prop.substring(0, indexOfNoFlip); - newStyle[newProp] = style[prop]; - } else { - newStyle[prop] = style[prop]; - } + const indexOfNoFlip = prop.indexOf('$noI18n'); + + if (I18nManager.isRTL) { + if (PROPERTIES_TO_SWAP[prop]) { + const newProp = flipProperty(prop); + newStyle[newProp] = style[prop]; + } else if (PROPERTIES_SWAP_LEFT_RIGHT[prop]) { + newStyle[prop] = swapLeftRight(style[prop]); + } else if (PROPERTIES_SWAP_LTR_RTL[prop]) { + newStyle[prop] = swapLtrRtl(style[prop]); + } else if (prop === 'textShadowOffset') { + newStyle[prop] = style[prop]; + newStyle[prop].width = additiveInverse(style[prop].width); + } else if (prop === 'transform') { + newStyle[prop] = style[prop].map(flipTransform); + } else if (indexOfNoFlip > -1) { + const newProp = prop.substring(0, indexOfNoFlip); + newStyle[newProp] = style[prop]; } else { - if (indexOfNoFlip > -1) { - const newProp = prop.substring(0, indexOfNoFlip); - newStyle[newProp] = style[prop]; - } else { - newStyle[prop] = style[prop]; - } + newStyle[prop] = style[prop]; + } + } else { + if (indexOfNoFlip > -1) { + const newProp = prop.substring(0, indexOfNoFlip); + newStyle[newProp] = style[prop]; + } else { + newStyle[prop] = style[prop]; } } } diff --git a/src/apis/UIManager/index.js b/src/apis/UIManager/index.js index db5fe173..a11bbcb5 100644 --- a/src/apis/UIManager/index.js +++ b/src/apis/UIManager/index.js @@ -35,8 +35,11 @@ const UIManager = { updateView(node, props, component /* only needed to surpress React errors in development */) { for (const prop in props) { - const value = props[prop]; + if (!Object.prototype.hasOwnProperty.call(props, prop)) { + continue; + } + const value = props[prop]; switch (prop) { case 'style': // convert styles to DOM-styles diff --git a/src/components/Image/index.js b/src/components/Image/index.js index 57b9903e..38c4d52b 100644 --- a/src/components/Image/index.js +++ b/src/components/Image/index.js @@ -8,6 +8,8 @@ import StyleSheetPropType from '../../propTypes/StyleSheetPropType'; import View from '../View'; import React, { Component, PropTypes } from 'react'; +const emptyObject = {}; + const STATUS_ERRORED = 'ERRORED'; const STATUS_LOADED = 'LOADED'; const STATUS_LOADING = 'LOADING'; @@ -52,7 +54,7 @@ class Image extends Component { }; static defaultProps = { - style: {} + style: emptyObject }; static resizeMode = ImageResizeMode; diff --git a/src/components/ScrollView/index.js b/src/components/ScrollView/index.js index 0e70e45b..c8f5bd87 100644 --- a/src/components/ScrollView/index.js +++ b/src/components/ScrollView/index.js @@ -17,6 +17,8 @@ import View from '../View'; import ViewStylePropTypes from '../View/ViewStylePropTypes'; import React, { Component, PropTypes } from 'react'; +const emptyObject = {}; + /* eslint-disable react/prefer-es6-class */ const ScrollView = React.createClass({ propTypes: { @@ -79,7 +81,7 @@ const ScrollView = React.createClass({ if (typeof y === 'number') { console.warn('`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, animated: true})` instead.'); } else { - ({ x, y, animated } = y || {}); + ({ x, y, animated } = y || emptyObject); } this.getScrollResponder().scrollResponderScrollTo({ x: x || 0, y: y || 0, animated: animated !== false }); diff --git a/src/components/Switch/index.js b/src/components/Switch/index.js index 45ad85ef..dbde8071 100644 --- a/src/components/Switch/index.js +++ b/src/components/Switch/index.js @@ -7,6 +7,7 @@ import UIManager from '../../apis/UIManager'; import View from '../View'; import React, { Component, PropTypes } from 'react'; +const emptyObject = {}; const thumbDefaultBoxShadow = '0px 1px 3px rgba(0,0,0,0.5)'; const thumbFocusedBoxShadow = `${thumbDefaultBoxShadow}, 0 0 0 10px rgba(0,0,0,0.1)`; @@ -28,7 +29,7 @@ class Switch extends Component { activeThumbColor: '#009688', activeTrackColor: '#A3D3CF', disabled: false, - style: {}, + style: emptyObject, thumbColor: '#FAFAFA', trackColor: '#939393', value: false diff --git a/src/components/Text/index.js b/src/components/Text/index.js index 7c906fdc..91bbde1e 100644 --- a/src/components/Text/index.js +++ b/src/components/Text/index.js @@ -40,25 +40,24 @@ class Text extends Component { onLayout, suppressHighlighting, /* eslint-enable */ - ...other + ...otherProps } = this.props; if (onPress) { - other.onClick = onPress; - other.onKeyDown = this._createEnterHandler(onPress); - other.tabIndex = 0; + otherProps.onClick = onPress; + otherProps.onKeyDown = this._createEnterHandler(onPress); + otherProps.tabIndex = 0; } - return createDOMElement('span', { - ...other, - style: [ - styles.initial, - style, - !selectable && styles.notSelectable, - numberOfLines === 1 && styles.singleLineStyle, - onPress && styles.pressable - ] - }); + otherProps.style = [ + styles.initial, + style, + !selectable && styles.notSelectable, + numberOfLines === 1 && styles.singleLineStyle, + onPress && styles.pressable + ]; + + return createDOMElement('span', otherProps); } _createEnterHandler(fn) { diff --git a/src/components/TextInput/index.js b/src/components/TextInput/index.js index 4f0adcb7..2432b300 100644 --- a/src/components/TextInput/index.js +++ b/src/components/TextInput/index.js @@ -10,6 +10,8 @@ import UIManager from '../../apis/UIManager'; import View from '../View'; import { Component, PropTypes } from 'react'; +const emptyObject = {}; + /** * React Native events differ from W3C events. */ @@ -94,7 +96,7 @@ class TextInput extends Component { multiline: false, numberOfLines: 2, secureTextEntry: false, - style: {} + style: emptyObject }; blur() { @@ -153,7 +155,7 @@ class TextInput extends Component { selectionColor, selectTextOnFocus, /* eslint-enable */ - ...other + ...otherProps } = this.props; let type; @@ -186,8 +188,7 @@ class TextInput extends Component { const component = multiline ? TextareaAutosize : 'input'; - const props = { - ...other, + Object.assign(otherProps, { autoCorrect: autoCorrect ? 'on' : 'off', dir: 'auto', onBlur: normalizeEventHandler(this._handleBlur), @@ -201,16 +202,16 @@ class TextInput extends Component { styles.initial, style ] - }; + }); if (multiline) { - props.maxRows = maxNumberOfLines || numberOfLines; - props.minRows = numberOfLines; + otherProps.maxRows = maxNumberOfLines || numberOfLines; + otherProps.minRows = numberOfLines; } else { - props.type = type; + otherProps.type = type; } - return createDOMElement(component, props); + return createDOMElement(component, otherProps); } _handleBlur = (e) => { @@ -245,7 +246,7 @@ class TextInput extends Component { } _handleSelectionChange = (e) => { - const { onSelectionChange, selection = {} } = this.props; + const { onSelectionChange, selection = emptyObject } = this.props; if (onSelectionChange) { try { const node = e.target; diff --git a/src/components/View/index.js b/src/components/View/index.js index e330aea5..f021b7de 100644 --- a/src/components/View/index.js +++ b/src/components/View/index.js @@ -99,7 +99,7 @@ class View extends Component { onMagicTap, removeClippedSubviews, /* eslint-enable */ - ...other + ...otherProps } = this.props; const flattenedStyle = StyleSheet.flatten(style); @@ -107,27 +107,24 @@ class View extends Component { // 'View' needs to set 'flexShrink:0' only when there is no 'flex' or 'flexShrink' style provided const needsFlexReset = !flattenedStyle || (flattenedStyle.flex == null && flattenedStyle.flexShrink == null); - const normalizedEventHandlers = eventHandlerNames.reduce((handlerProps, handlerName) => { + const component = this.context.isInAButtonView ? 'span' : 'div'; + + eventHandlerNames.reduce((props, handlerName) => { const handler = this.props[handlerName]; if (typeof handler === 'function') { - handlerProps[handlerName] = this._normalizeEventForHandler(handler, handlerName); + props[handlerName] = this._normalizeEventForHandler(handler, handlerName); } - return handlerProps; - }, {}); + return props; + }, otherProps); - const component = this.context.isInAButtonView ? 'span' : 'div'; - const props = { - ...other, - ...normalizedEventHandlers, - style: [ - styles.initial, - style, - needsFlexReset && styles.flexReset, - pointerEventsStyle - ] - }; + otherProps.style = [ + styles.initial, + style, + needsFlexReset && styles.flexReset, + pointerEventsStyle + ]; - return createDOMElement(component, props); + return createDOMElement(component, otherProps); } _normalizeEventForHandler(handler, handlerName) { diff --git a/src/modules/ScrollResponder/index.js b/src/modules/ScrollResponder/index.js index 5f0bf100..8ff42b39 100644 --- a/src/modules/ScrollResponder/index.js +++ b/src/modules/ScrollResponder/index.js @@ -105,6 +105,8 @@ var warning = require('fbjs/lib/warning'); * this.props.onKeyboardDidHide */ +const emptyObject = {}; + var IS_ANIMATING_TOUCH_START_THRESHOLD_MS = 16; type State = { @@ -378,7 +380,7 @@ var ScrollResponderMixin = { if (typeof x === 'number') { console.warn('`scrollResponderScrollTo(x, y, animated)` is deprecated. Use `scrollResponderScrollTo({x: 5, y: 5, animated: true})` instead.'); } else { - ({x, y, animated} = x || {}); + ({x, y, animated} = x || emptyObject); } const node = this.scrollResponderGetScrollableNode() node.scrollLeft = x || 0 diff --git a/src/modules/applyLayout/index.js b/src/modules/applyLayout/index.js index 7213773a..48c88e30 100644 --- a/src/modules/applyLayout/index.js +++ b/src/modules/applyLayout/index.js @@ -7,13 +7,15 @@ import emptyFunction from 'fbjs/lib/emptyFunction'; +const emptyObject = {}; + const applyLayout = (Component) => { const componentDidMount = Component.prototype.componentDidMount || emptyFunction; const componentDidUpdate = Component.prototype.componentDidUpdate || emptyFunction; Component.prototype.componentDidMount = function () { componentDidMount.call(this); - this._layoutState = {}; + this._layoutState = emptyObject; this._handleLayout(); }; diff --git a/src/modules/createDOMElement/index.js b/src/modules/createDOMElement/index.js index 86ad1faa..19565987 100644 --- a/src/modules/createDOMElement/index.js +++ b/src/modules/createDOMElement/index.js @@ -1,6 +1,8 @@ import React from 'react'; import StyleSheet from '../../apis/StyleSheet'; +const emptyObject = {}; + const roleComponents = { article: 'article', banner: 'header', @@ -17,7 +19,7 @@ const roleComponents = { region: 'section' }; -const createDOMElement = (component, rnProps = {}) => { +const createDOMElement = (component, rnProps = emptyObject) => { const { accessibilityLabel, accessibilityLiveRegion, @@ -25,15 +27,14 @@ const createDOMElement = (component, rnProps = {}) => { accessible = true, testID, type, - ...other + ...domProps } = rnProps; const accessibilityComponent = accessibilityRole && roleComponents[accessibilityRole]; const Component = accessibilityComponent || component; - const domProps = { - ...other, - ...StyleSheet.resolve(other) - }; + + Object.assign(domProps, StyleSheet.resolve(domProps)); + if (!accessible) { domProps['aria-hidden'] = true; } if (accessibilityLabel) { domProps['aria-label'] = accessibilityLabel; } if (accessibilityLiveRegion) { domProps['aria-live'] = accessibilityLiveRegion; } diff --git a/src/modules/normalizeNativeEvent.js b/src/modules/normalizeNativeEvent.js index 446385a2..d70490b1 100644 --- a/src/modules/normalizeNativeEvent.js +++ b/src/modules/normalizeNativeEvent.js @@ -1,5 +1,7 @@ +const emptyArray = []; + // Mobile Safari re-uses touch objects, so we copy the properties we want and normalize the identifier -const normalizeTouches = (touches = []) => Array.prototype.slice.call(touches).map((touch) => { +const normalizeTouches = (touches = emptyArray) => Array.prototype.slice.call(touches).map((touch) => { const identifier = touch.identifier > 20 ? (touch.identifier % 20) : touch.identifier; const rect = touch.target && touch.target.getBoundingClientRect(); @@ -59,21 +61,23 @@ function normalizeTouchEvent(nativeEvent) { } function normalizeMouseEvent(nativeEvent) { - const touches = [ { - _normalized: true, - clientX: nativeEvent.clientX, - clientY: nativeEvent.clientY, - force: nativeEvent.force, - locationX: nativeEvent.clientX, - locationY: nativeEvent.clientY, - identifier: 0, - pageX: nativeEvent.pageX, - pageY: nativeEvent.pageY, - screenX: nativeEvent.screenX, - screenY: nativeEvent.screenY, - target: nativeEvent.target, - timestamp: Date.now() - } ]; + const touches = [ + { + _normalized: true, + clientX: nativeEvent.clientX, + clientY: nativeEvent.clientY, + force: nativeEvent.force, + locationX: nativeEvent.clientX, + locationY: nativeEvent.clientY, + identifier: 0, + pageX: nativeEvent.pageX, + pageY: nativeEvent.pageY, + screenX: nativeEvent.screenX, + screenY: nativeEvent.screenY, + target: nativeEvent.target, + timestamp: Date.now() + } + ]; return { _normalized: true, changedTouches: touches, @@ -87,7 +91,7 @@ function normalizeMouseEvent(nativeEvent) { stopPropagation: nativeEvent.stopPropagation.bind(nativeEvent), target: nativeEvent.target, timestamp: touches[0].timestamp, - touches: (nativeEvent.type === 'mouseup') ? [] : touches + touches: (nativeEvent.type === 'mouseup') ? emptyArray : touches }; }