From d75bd44e2780b8409bab68a3e7ea240fd79b9c04 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Tue, 31 Mar 2015 09:50:46 -0700 Subject: [PATCH] [react-native] Bring React.render behavior in line with web --- Libraries/ReactIOS/ReactIOS.js | 8 +++-- Libraries/ReactIOS/ReactIOSMount.js | 56 +++++++++++++++++++---------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/Libraries/ReactIOS/ReactIOS.js b/Libraries/ReactIOS/ReactIOS.js index 028577429..56faaa30b 100644 --- a/Libraries/ReactIOS/ReactIOS.js +++ b/Libraries/ReactIOS/ReactIOS.js @@ -67,8 +67,12 @@ var augmentElement = function(element: ReactElement) { return element; }; -var render = function(component: ReactComponent, mountInto: number) { - ReactIOSMount.renderComponent(component, mountInto); +var render = function( + element: ReactElement, + mountInto: number, + callback?: ?(() => void) +): ?ReactComponent { + return ReactIOSMount.renderComponent(element, mountInto, callback); }; var ReactIOS = { diff --git a/Libraries/ReactIOS/ReactIOSMount.js b/Libraries/ReactIOS/ReactIOSMount.js index 7e4094408..730031672 100644 --- a/Libraries/ReactIOS/ReactIOSMount.js +++ b/Libraries/ReactIOS/ReactIOSMount.js @@ -16,13 +16,13 @@ var RCTUIManager = require('NativeModules').UIManager; var ReactIOSTagHandles = require('ReactIOSTagHandles'); var ReactPerf = require('ReactPerf'); var ReactReconciler = require('ReactReconciler'); +var ReactUpdateQueue = require('ReactUpdateQueue'); var ReactUpdates = require('ReactUpdates'); var emptyObject = require('emptyObject'); var instantiateReactComponent = require('instantiateReactComponent'); var invariant = require('invariant'); - -var TOP_ROOT_NODE_IDS = {}; +var shouldUpdateReactComponent = require('shouldUpdateReactComponent'); function instanceNumberToChildRootID(rootNodeID, instanceNumber) { return rootNodeID + '[' + instanceNumber + ']'; @@ -85,10 +85,26 @@ var ReactIOSMount = { * @param {containerTag} containerView Handle to native view tag */ renderComponent: function( - descriptor: ReactComponent, - containerTag: number - ) { - var instance = instantiateReactComponent(descriptor); + nextElement: ReactElement, + containerTag: number, + callback?: ?(() => void) + ): ?ReactComponent { + var topRootNodeID = ReactIOSTagHandles.tagToRootNodeID[containerTag]; + if (topRootNodeID) { + var prevComponent = ReactIOSMount._instancesByContainerID[topRootNodeID]; + if (prevComponent) { + var prevElement = prevComponent._currentElement; + if (shouldUpdateReactComponent(prevElement, nextElement)) { + ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement); + if (callback) { + ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback); + } + return prevComponent; + } else { + ReactIOSMount.unmountComponentAtNode(containerTag); + } + } + } if (!ReactIOSTagHandles.reactTagIsNativeTopRootID(containerTag)) { console.error('You cannot render into anything but a top root'); @@ -100,13 +116,14 @@ var ReactIOSMount = { topRootNodeID, containerTag ); - TOP_ROOT_NODE_IDS[topRootNodeID] = true; + + var instance = instantiateReactComponent(nextElement); + ReactIOSMount._instancesByContainerID[topRootNodeID] = instance; var childRootNodeID = instanceNumberToChildRootID( topRootNodeID, ReactIOSMount.instanceCount++ ); - ReactIOSMount._instancesByContainerID[topRootNodeID] = instance; // The initial render is synchronous but any updates that happen during // rendering, in componentWillMount or componentDidMount, will be batched @@ -118,6 +135,11 @@ var ReactIOSMount = { childRootNodeID, topRootNodeID ); + var component = instance.getPublicInstance(); + if (callback) { + callback.call(component); + } + return component; }, /** @@ -170,20 +192,18 @@ var ReactIOSMount = { * component at this time. */ unmountComponentAtNode: function(containerTag: number): bool { - var containerID = ReactIOSTagHandles.tagToRootNodeID[containerTag]; + if (!ReactIOSTagHandles.reactTagIsNativeTopRootID(containerTag)) { + console.error('You cannot render into anything but a top root'); + return; + } - invariant( - TOP_ROOT_NODE_IDS[containerID], - 'We only currently support removing components from the root node' - ); + var containerID = ReactIOSTagHandles.tagToRootNodeID[containerTag]; var instance = ReactIOSMount._instancesByContainerID[containerID]; if (!instance) { - console.error('Tried to unmount a component that does not exist'); return false; } ReactIOSMount.unmountComponentFromNode(instance, containerID); delete ReactIOSMount._instancesByContainerID[containerID]; - delete TOP_ROOT_NODE_IDS[containerID]; return true; }, @@ -200,10 +220,8 @@ var ReactIOSMount = { instance: ReactComponent, containerID: string ) { - // call back into native to remove all of the subviews from this container - // TODO: ReactComponent.prototype.unmountComponent is missing from Flow's - // react lib. - (instance: any).unmountComponent(); + // Call back into native to remove all of the subviews from this container + ReactReconciler.unmountComponent(instance); var containerTag = ReactIOSTagHandles.mostRecentMountedNodeHandleForRootNodeID(containerID); RCTUIManager.removeSubviewsFromContainerWithID(containerTag);