react-native/Libraries/ReactIOS/NativeMethodsMixin.js

171 lines
4.7 KiB
JavaScript

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule NativeMethodsMixin
* @flow
*/
'use strict';
var NativeModules = require('NativeModules');
var RCTUIManager = NativeModules.UIManager;
var TextInputState = require('TextInputState');
var findNodeHandle = require('findNodeHandle');
var flattenStyle = require('flattenStyle');
var invariant = require('invariant');
var mergeFast = require('mergeFast');
var precomputeStyle = require('precomputeStyle');
type MeasureOnSuccessCallback = (
x: number,
y: number,
width: number,
height: number,
pageX: number,
pageY: number
) => void
type MeasureLayoutOnSuccessCallback = (
left: number,
top: number,
width: number,
height: number
) => void
var NativeMethodsMixin = {
measure: function(callback: MeasureOnSuccessCallback) {
RCTUIManager.measure(
findNodeHandle(this),
mountSafeCallback(this, callback)
);
},
measureLayout: function(
relativeToNativeNode: number,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail: () => void /* currently unused */
) {
RCTUIManager.measureLayout(
findNodeHandle(this),
relativeToNativeNode,
mountSafeCallback(this, onFail),
mountSafeCallback(this, onSuccess)
);
},
/**
* This function sends props straight to native. They will not participate
* in future diff process, this means that if you do not include them in the
* next render, they will remain active.
*/
setNativeProps: function(nativeProps: Object) {
// nativeProps contains a style attribute that's going to be flattened
// and all the attributes expanded in place. In order to make this
// process do as few allocations and copies as possible, we return
// one if the other is empty. Only if both have values then we create
// a new object and merge.
var hasOnlyStyle = true;
for (var key in nativeProps) {
if (key !== 'style') {
hasOnlyStyle = false;
break;
}
}
var validAttributes = this.viewConfig.validAttributes;
var hasProcessedProps = false;
var processedProps = {};
for (var key in nativeProps) {
var process = validAttributes[key] && validAttributes[key].process;
if (process) {
hasProcessedProps = true;
processedProps[key] = process(nativeProps[key]);
}
}
var style = precomputeStyle(
flattenStyle(processedProps.style || nativeProps.style),
this.viewConfig.validAttributes
);
var props = null;
if (hasOnlyStyle) {
props = style;
} else {
props = nativeProps;
if (hasProcessedProps) {
props = mergeFast(props, processedProps);
}
if (style) {
props = mergeFast(props, style);
}
}
RCTUIManager.updateView(
findNodeHandle(this),
this.viewConfig.uiViewClassName,
props
);
},
focus: function() {
TextInputState.focusTextInput(findNodeHandle(this));
},
blur: function() {
TextInputState.blurTextInput(findNodeHandle(this));
}
};
function throwOnStylesProp(component, props) {
if (props.styles !== undefined) {
var owner = component._owner || null;
var name = component.constructor.displayName;
var msg = '`styles` is not a supported property of `' + name + '`, did ' +
'you mean `style` (singular)?';
if (owner && owner.constructor && owner.constructor.displayName) {
msg += '\n\nCheck the `' + owner.constructor.displayName + '` parent ' +
' component.';
}
throw new Error(msg);
}
}
if (__DEV__) {
// hide this from Flow since we can't define these properties outside of
// __DEV__ without actually implementing them (setting them to undefined
// isn't allowed by ReactClass)
var NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
invariant(
!NativeMethodsMixin_DEV.componentWillMount &&
!NativeMethodsMixin_DEV.componentWillReceiveProps,
'Do not override existing functions.'
);
NativeMethodsMixin_DEV.componentWillMount = function () {
throwOnStylesProp(this, this.props);
};
NativeMethodsMixin_DEV.componentWillReceiveProps = function (newProps) {
throwOnStylesProp(this, newProps);
};
}
/**
* In the future, we should cleanup callbacks by cancelling them instead of
* using this.
*/
var mountSafeCallback = function(context: ReactComponent, callback: ?Function): any {
return function() {
if (!callback || (context.isMounted && !context.isMounted())) {
return;
}
return callback.apply(context, arguments);
};
};
module.exports = NativeMethodsMixin;