From d0790fea39ccc755abb635ff72819831264cff29 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Mon, 2 Oct 2017 16:29:42 -0700 Subject: [PATCH] Introducting `UIManager.setLocalData()`, the way to provide environmental data to ShadowNode Summary: In some cases we need a way to provide some peice of data to shadow node to improve layout (or do something similar), `setLocalData` allows to do this. Reviewed By: AaaChiuuu Differential Revision: D5828368 fbshipit-source-id: bf6a04f460dc7695a16269426d365b78909bc8eb --- .../react/uimanager/ReactShadowNode.java | 12 ++++++++ .../react/uimanager/ReactShadowNodeImpl.java | 12 ++++++++ .../react/uimanager/UIImplementation.java | 29 +++++++++++++++---- .../react/uimanager/UIManagerModule.java | 23 +++++++++++++++ 4 files changed, 71 insertions(+), 5 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java index 08526b387..de2b44251 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNode.java @@ -181,6 +181,18 @@ public interface ReactShadowNode { boolean isDescendantOf(T ancestorNode); + /* + * In some cases we need a way to specify some environmental data to shadow node + * to improve layout (or do something similar), so {@code localData} serves these needs. + * For example, any stateful embedded native views may benefit from this. + * Have in mind that this data is not supposed to interfere with the state of + * the shadow node. + * Please respect one-directional data flow of React. + * Use {@link UIManagerModule#setViewLocalData} to set this property + * (to provide local/environmental data for a shadow node) from the main thread. + */ + public void setLocalData(Object data); + /** * Returns the offset within the native children owned by all layout-only nodes in the subtree * rooted at this node for the given child. Put another way, this returns the number of native diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java index b82478140..55c49d70e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java @@ -518,6 +518,18 @@ public class ReactShadowNodeImpl implements ReactShadowNode return isDescendant; } + /* + * In some cases we need a way to specify some environmental data to shadow node + * to improve layout (or do something similar), so {@code localData} serves these needs. + * For example, any stateful embedded native views may benefit from this. + * Have in mind that this data is not supposed to interfere with the state of + * the shadow node. + * Please respect one-directional data flow of React. + * Use {@link ReactUIManagerModule#setViewLocalData} to set this property + * (to provide local/environmental data for a shadow node) from the main thread. + */ + public void setLocalData(Object data) {} + /** * Returns the offset within the native children owned by all layout-only nodes in the subtree * rooted at this node for the given child. Put another way, this returns the number of native diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java index 9813184b4..bd2996f85 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIImplementation.java @@ -236,12 +236,20 @@ public class UIImplementation { cssNode.setStyleWidth(newWidth); cssNode.setStyleHeight(newHeight); - // If we're in the middle of a batch, the change will automatically be dispatched at the end of - // the batch. As all batches are executed as a single runnable on the event queue this should - // always be empty, but that calling architecture is an implementation detail. - if (mOperationsQueue.isEmpty()) { - dispatchViewUpdates(-1); // -1 = no associated batch id + dispatchViewUpdatesIfNeeded(); + } + + public void setViewLocalData(int tag, Object data) { + ReactShadowNode shadowNode = mShadowNodeRegistry.getNode(tag); + + if (shadowNode == null) { + throw new IllegalViewOperationException( + "Trying to set local data for view with unknown tag: " + tag); } + + shadowNode.setLocalData(data); + + dispatchViewUpdatesIfNeeded(); } public void profileNextBatch() { @@ -641,6 +649,17 @@ public class UIImplementation { } } + private void dispatchViewUpdatesIfNeeded() { + // If we are in the middle of a batch update, any additional changes + // will automatically be dispatched at the end of the batch. + // If we are not, we have to initiate new batch update. + // As all batches are executed as a single runnable on the event queue + // this should always be empty, but that calling architecture is an implementation detail. + if (mOperationsQueue.isEmpty()) { + dispatchViewUpdates(-1); // "-1" means "no associated batch id" + } + } + protected void updateViewHierarchy() { Systrace.beginSection( Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index f2982d892..f2aba52c5 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -324,6 +324,29 @@ public class UIManagerModule extends ReactContextBaseJavaModule implements mUIImplementation.updateNodeSize(nodeViewTag, newWidth, newHeight); } + /** + * Sets local data for a shadow node corresponded with given tag. + * In some cases we need a way to specify some environmental data to shadow node + * to improve layout (or do something similar), so {@code localData} serves these needs. + * For example, any stateful embedded native views may benefit from this. + * Have in mind that this data is not supposed to interfere with the state of + * the shadow view. + * Please respect one-directional data flow of React. + */ + public void setViewLocalData(final int tag, final Object data) { + final ReactApplicationContext reactApplicationContext = getReactApplicationContext(); + + reactApplicationContext.assertOnUiQueueThread(); + + reactApplicationContext.runUIBackgroundRunnable( + new GuardedRunnable(reactApplicationContext) { + @Override + public void runGuarded() { + mUIImplementation.setViewLocalData(tag, data); + } + }); + } + @ReactMethod public void createView(int tag, String className, int rootViewTag, ReadableMap props) { if (DEBUG) {