Backout D2677289 [react_native] View recycling in JS

Summary: public

We're seeing related crashes. The diff has no tests, the perf tests weren't conclusive, and the person who'd be supporting it no longer is available to work on it. We can try this again later in a less rushed manner with proper perf testing.

Reviewed By: davidaurelio

Differential Revision: D2696615

fb-gh-sync-id: 3b6814ac12af19516146d5c42d2add8321b10db5
This commit is contained in:
Andy Street
2015-11-26 08:36:02 -08:00
committed by facebook-github-bot-0
parent bb11e05c33
commit a636ddd9f0
12 changed files with 27 additions and 345 deletions

View File

@@ -16,7 +16,6 @@ var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
var ReactNativeStyleAttributes = require('ReactNativeStyleAttributes');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactNativeViewPool = require('ReactNativeViewPool');
var ReactMultiChild = require('ReactMultiChild');
var RCTUIManager = require('NativeModules').UIManager;
@@ -89,7 +88,6 @@ ReactNativeBaseComponent.Mixin = {
unmountComponent: function() {
deleteAllListeners(this._rootNodeID);
this.unmountChildren();
ReactNativeViewPool.release(this);
this._rootNodeID = null;
},
@@ -206,7 +204,24 @@ ReactNativeBaseComponent.Mixin = {
mountComponent: function(rootID, transaction, context) {
this._rootNodeID = rootID;
var tag = ReactNativeViewPool.acquire(this);
var tag = ReactNativeTagHandles.allocateTag();
if (__DEV__) {
deepFreezeAndThrowOnMutationInDev(this._currentElement.props);
}
var updatePayload = ReactNativeAttributePayload.create(
this._currentElement.props,
this.viewConfig.validAttributes
);
var nativeTopRootID = ReactNativeTagHandles.getNativeTopRootIDFromNodeID(rootID);
RCTUIManager.createView(
tag,
this.viewConfig.uiViewClassName,
nativeTopRootID ? ReactNativeTagHandles.rootNodeIDToTag[nativeTopRootID] : null,
updatePayload
);
this._registerListenersUponCreation(this._currentElement.props);
this.initializeChildren(

View File

@@ -15,7 +15,6 @@ var RCTUIManager = require('NativeModules').UIManager;
var ReactElement = require('ReactElement');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactNativeViewPool = require('ReactNativeViewPool');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
@@ -217,7 +216,6 @@ var ReactNativeMount = {
ReactNativeMount.unmountComponentAtNode(containerTag);
// call back into native to remove all of the subviews from this container
RCTUIManager.removeRootView(containerTag);
ReactNativeViewPool.clearPoolForRootView(containerTag);
},
/**

View File

@@ -14,7 +14,6 @@
var CallbackQueue = require('CallbackQueue');
var PooledClass = require('PooledClass');
var Transaction = require('Transaction');
var ReactNativeViewPool = require('ReactNativeViewPool');
/**
* Provides a `CallbackQueue` queue for collecting `onDOMReady` callbacks during
@@ -36,18 +35,12 @@ var ON_DOM_READY_QUEUEING = {
}
};
var RN_VIEW_POOL_WRAPPER = {
close: function() {
ReactNativeViewPool.onReconcileTransactionClose();
},
};
/**
* Executed within the scope of the `Transaction` instance. Consider these as
* being member methods, but with an implied ordering while being isolated from
* each other.
*/
var TRANSACTION_WRAPPERS = [ON_DOM_READY_QUEUEING, RN_VIEW_POOL_WRAPPER];
var TRANSACTION_WRAPPERS = [ON_DOM_READY_QUEUEING];
/**
* Currently:

View File

@@ -1,264 +0,0 @@
/**
* 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 ReactNativeViewPool
*/
'use strict';
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactNativeAttributePayload = require('ReactNativeAttributePayload');
var RCTUIManager = require('NativeModules').UIManager;
var Platform = require('Platform');
var deepFreezeAndThrowOnMutationInDev = require('deepFreezeAndThrowOnMutationInDev');
var emptyFunction = require('emptyFunction');
var flattenStyle = require('flattenStyle');
var EMPTY_POOL = [[]];
var ENABLED = !!RCTUIManager.dropViews;
/* indicies used for _addToPool arrays */
var TAGS_IDX = 0;
var KEYS_IDX = 1;
var PROPS_IDX = 2;
var _pools = {};
var _poolSize = {};
var layoutOnlyProps = RCTUIManager.layoutOnlyProps;
function isCollapsableForStyle(style) {
var flatStyle = flattenStyle(style);
for (var styleKey in flatStyle) {
if (layoutOnlyProps[styleKey] !== true) {
return false;
}
}
return true;
}
function isCollapsable(viewRef) {
var props = viewRef._currentElement.props;
if (props.collapsable !== undefined && !props.collapsable) {
return false;
}
var validAttributes = viewRef.viewConfig.validAttributes;
for (var propKey in props) {
if (!!validAttributes[propKey] && propKey !== 'style' && propKey !== 'collapsable') {
return false;
}
}
return !props.style || isCollapsableForStyle(viewRef._currentElement.props.style);
}
function enqueueCreate(viewRef, rootTag) {
var tag = ReactNativeTagHandles.allocateTag();
if (__DEV__) {
deepFreezeAndThrowOnMutationInDev(viewRef._currentElement.props);
}
var updatePayload = ReactNativeAttributePayload.create(
viewRef._currentElement.props,
viewRef.viewConfig.validAttributes
);
RCTUIManager.createView(
tag,
viewRef.viewConfig.uiViewClassName,
rootTag,
updatePayload
);
return tag;
}
function getViewTag(viewRef) {
return ReactNativeTagHandles.mostRecentMountedNodeHandleForRootNodeID(viewRef._rootNodeID);
}
function getViewProps(viewRef) {
return viewRef._currentElement.props;
}
function getViewValidAttributes(viewRef) {
return viewRef.viewConfig.validAttributes;
}
function getRootViewTag(viewRef) {
var nativeTopRootID = ReactNativeTagHandles.getNativeTopRootIDFromNodeID(viewRef._rootNodeID);
return ReactNativeTagHandles.rootNodeIDToTag[nativeTopRootID];
}
function poolKey(viewRef) {
var viewClass = viewRef.viewConfig.uiViewClassName;
if (Platform.OS === 'android' && viewClass === 'RCTView') {
return isCollapsable(viewRef) ? 'CollapsedRCTView' : 'RCTView';
}
return viewClass;
}
class ReactNativeViewPool {
constructor() {
this._pool = {};
this._poolQueue = {};
this._addToPool = [[],[],[]];
this._viewsToDelete = [];
if (__DEV__) {
this._recycleStats = {};
this._deleteStats = {};
}
}
onReconcileTransactionClose() {
// flush all deletes, move object from pool_queue to the actual pool
if (this._viewsToDelete.length > 0) {
RCTUIManager.dropViews(this._viewsToDelete);
}
var addToPoolTags = this._addToPool[TAGS_IDX];
var addToPoolKeys = this._addToPool[KEYS_IDX];
var addToPoolProps = this._addToPool[PROPS_IDX];
for (var i = addToPoolTags.length - 1; i >= 0; i--) {
var nativeTag = addToPoolTags[i];
var key = addToPoolKeys[i];
var props = addToPoolProps[i];
var views = this._pool[key] || [[],[]];
views[0].push(nativeTag);
views[1].push(props);
this._pool[key] = views;
}
this._viewsToDelete = [];
this._addToPool = [[],[],[]];
this._poolQueue = {};
}
acquire(viewRef, rootTag) {
var key = poolKey(viewRef);
if ((this._pool[key] || EMPTY_POOL)[0].length) {
var views = this._pool[key];
var nativeTag = views[0].pop();
var oldProps = views[1].pop();
var updatePayload = ReactNativeAttributePayload.diff(
oldProps,
getViewProps(viewRef),
getViewValidAttributes(viewRef)
);
if (__DEV__) {
this._recycleStats[key] = (this._recycleStats[key] || 0) + 1;
}
if (updatePayload) {
RCTUIManager.updateView(
nativeTag,
viewRef.viewConfig.uiViewClassName,
updatePayload
);
}
return nativeTag;
} else {
// If there is no view available for the given pool key, we just enqueue call to create one
return enqueueCreate(viewRef, rootTag);
}
}
release(viewRef) {
var key = poolKey(viewRef);
var nativeTag = getViewTag(viewRef);
var pooledCount = (this._pool[key] || EMPTY_POOL)[0].length + (this._poolQueue[key] || 0);
if (pooledCount < (_poolSize[key] || 0)) {
// we have room in the pool for this view
// we can add it to the queue so that it will be added to the actual pull in
// onReconcileTransactionClose
this._addToPool[TAGS_IDX].push(nativeTag);
this._addToPool[KEYS_IDX].push(key);
this._addToPool[PROPS_IDX].push(getViewProps(viewRef));
this._poolQueue[key] = (this._poolQueue[key] || 0) + 1;
} else {
if (__DEV__) {
if (_poolSize[key]) {
this._deleteStats[key] = (this._deleteStats[key] || 0) + 1;
}
}
this._viewsToDelete.push(nativeTag);
}
}
clear() {
for (var key in this._pool) {
var poolTags = this._pool[key][0];
for (var i = poolTags.length - 1; i >= 0; i--) {
this._viewsToDelete.push(poolTags[i]);
}
}
var addToPoolTags = this._addToPool[0];
for (var i = addToPoolTags.length - 1; i >= 0; i--) {
this._viewsToDelete.push(addToPoolTags[i]);
}
this._addToPool = [[],[],[]];
this.onReconcileTransactionClose();
}
printStats() {
if (__DEV__) {
console.log('Stats', this._recycleStats, this._deleteStats);
}
}
}
module.exports = {
onReconcileTransactionClose: function() {
if (ENABLED) {
for (var pool in _pools) {
_pools[pool].onReconcileTransactionClose();
}
}
},
acquire: function(viewRef) {
var rootTag = getRootViewTag(viewRef);
if (ENABLED) {
var pool = _pools[rootTag];
if (!pool) {
pool = _pools[rootTag] = new ReactNativeViewPool();
}
return pool.acquire(viewRef, rootTag);
} else {
return enqueueCreate(viewRef, rootTag);
}
},
release: ENABLED ? function(viewRef) {
var pool = _pools[getRootViewTag(viewRef)];
if (pool) {
pool.release(viewRef);
}
} : emptyFunction,
clearPoolForRootView: ENABLED ? function(rootID) {
var pool = _pools[rootID];
if (pool) {
pool.clear();
delete _pools[rootID];
}
} : emptyFunction,
configure: function(pool_size) {
_poolSize = pool_size;
},
printStats: function() {
if (__DEV__) {
console.log('Pool size', _poolSize);
for (var pool in _pools) {
_pools[pool].onReconcileTransactionClose();
}
}
},
};