Address avoidable object creation

This commit is contained in:
Nicolas Gallagher
2016-12-23 12:22:32 +00:00
parent dc7f526f6b
commit 1273bfc7cf
13 changed files with 119 additions and 100 deletions

View File

@@ -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<any, any, any>
@@ -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;
}

View File

@@ -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];
}
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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 });

View File

@@ -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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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

View File

@@ -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();
};

View File

@@ -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; }

View File

@@ -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
};
}