Allocate unique reactTags for RN and Fabric (#12587)

Took this opportunity to remove some abstract overhead.

In Fabric it is extra simple since they no longer overlap with root tags.
This commit is contained in:
Sebastian Markbåge
2018-04-09 18:41:13 -07:00
committed by GitHub
parent f88deda83b
commit 2f7bca0eb2
6 changed files with 29 additions and 65 deletions
+11 -3
View File
@@ -20,7 +20,6 @@ import * as ReactNativeAttributePayload from './ReactNativeAttributePayload';
import * as ReactNativeFrameScheduling from './ReactNativeFrameScheduling';
import * as ReactNativeViewConfigRegistry from './ReactNativeViewConfigRegistry';
import ReactFiberReconciler from 'react-reconciler';
import ReactNativeTagHandles from './ReactNativeTagHandles';
import deepFreezeAndThrowOnMutationInDev from 'deepFreezeAndThrowOnMutationInDev';
import emptyObject from 'fbjs/lib/emptyObject';
@@ -30,6 +29,12 @@ import TextInputState from 'TextInputState';
import FabricUIManager from 'FabricUIManager';
import UIManager from 'UIManager';
// Counter for uniquely identifying views.
// % 10 === 1 means it is a rootTag.
// % 2 === 0 means it is a Fabric tag.
// This means that they never overlap.
let nextReactTag = 2;
/**
* This is used for refs on host components.
*/
@@ -133,7 +138,9 @@ const ReactFabricRenderer = ReactFiberReconciler({
hostContext: {},
internalInstanceHandle: Object,
): Instance {
const tag = ReactNativeTagHandles.allocateTag();
const tag = nextReactTag;
nextReactTag += 2;
const viewConfig = ReactNativeViewConfigRegistry.get(type);
if (__DEV__) {
@@ -171,7 +178,8 @@ const ReactFabricRenderer = ReactFiberReconciler({
hostContext: {},
internalInstanceHandle: Object,
): TextInstance {
const tag = ReactNativeTagHandles.allocateTag();
const tag = nextReactTag;
nextReactTag += 2;
const node = FabricUIManager.createNode(
tag, // reactTag
@@ -13,7 +13,6 @@ import {batchedUpdates} from 'events/ReactGenericBatching';
import warning from 'fbjs/lib/warning';
import {getInstanceFromNode} from './ReactNativeComponentTree';
import ReactNativeTagHandles from './ReactNativeTagHandles';
import type {AnyNativeEvent} from 'events/PluginModuleType';
@@ -166,7 +165,7 @@ export function receiveTouches(
let rootNodeID = null;
const target = nativeEvent.target;
if (target !== null && target !== undefined) {
if (target < ReactNativeTagHandles.tagsStartAt) {
if (target < 1) {
if (__DEV__) {
warning(
false,
@@ -25,7 +25,6 @@ import {
} from './ReactNativeComponentTree';
import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
import * as ReactNativeFrameScheduling from './ReactNativeFrameScheduling';
import ReactNativeTagHandles from './ReactNativeTagHandles';
type Container = number;
export type Instance = {
@@ -36,6 +35,19 @@ export type Instance = {
type Props = Object;
type TextInstance = number;
// Counter for uniquely identifying views.
// % 10 === 1 means it is a rootTag.
// % 2 === 0 means it is a Fabric tag.
let nextReactTag = 3;
function allocateTag() {
let tag = nextReactTag;
if (tag % 10 === 1) {
tag += 2;
}
nextReactTag = tag + 2;
return tag;
}
function recursivelyUncacheFiberNode(node: Instance | TextInstance) {
if (typeof node === 'number') {
// Leaf node (eg text)
@@ -62,7 +74,7 @@ const NativeRenderer = ReactFiberReconciler({
hostContext: {},
internalInstanceHandle: Object,
): Instance {
const tag = ReactNativeTagHandles.allocateTag();
const tag = allocateTag();
const viewConfig = ReactNativeViewConfigRegistry.get(type);
if (__DEV__) {
@@ -101,7 +113,7 @@ const NativeRenderer = ReactFiberReconciler({
hostContext: {},
internalInstanceHandle: Object,
): TextInstance {
const tag = ReactNativeTagHandles.allocateTag();
const tag = allocateTag();
UIManager.createView(
tag, // reactTag
@@ -1,54 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import invariant from 'fbjs/lib/invariant';
/**
* Keeps track of allocating and associating native "tags" which are numeric,
* unique view IDs. All the native tags are negative numbers, to avoid
* collisions, but in the JS we keep track of them as positive integers to store
* them effectively in Arrays. So we must refer to them as "inverses" of the
* native tags (that are * normally negative).
*
* It *must* be the case that every `rootNodeID` always maps to the exact same
* `tag` forever. The easiest way to accomplish this is to never delete
* anything from this table.
* Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to
* unmount a component with a `rootNodeID`, then mount a new one in its place,
*/
const INITIAL_TAG_COUNT = 1;
const ReactNativeTagHandles = {
tagsStartAt: INITIAL_TAG_COUNT,
tagCount: INITIAL_TAG_COUNT,
allocateTag: function(): number {
// Skip over root IDs as those are reserved for native
while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
ReactNativeTagHandles.tagCount++;
}
const tag = ReactNativeTagHandles.tagCount;
ReactNativeTagHandles.tagCount++;
return tag;
},
assertRootTag: function(tag: number): void {
invariant(
this.reactTagIsNativeTopRootID(tag),
'Expect a native root tag, instead got %s',
tag,
);
},
reactTagIsNativeTopRootID: function(reactTag: number): boolean {
// We reserve all tags that are 1 mod 10 for native root views
return reactTag % 10 === 1;
},
};
export default ReactNativeTagHandles;
+1 -2
View File
@@ -9,7 +9,6 @@
// Mock of the Native Hooks
const ReactNativeTagHandles = require('../ReactNativeTagHandles').default;
const invariant = require('fbjs/lib/invariant');
// Map of viewTag -> {children: [childTag], parent: ?parentTag}
@@ -18,7 +17,7 @@ let views = new Map();
function autoCreateRoot(tag) {
// Seriously, this is how we distinguish roots in RN.
if (!views.has(tag) && ReactNativeTagHandles.reactTagIsNativeTopRootID(tag)) {
if (!views.has(tag) && tag % 10 === 1) {
roots.push(tag);
views.set(tag, {
children: [],
@@ -57,7 +57,7 @@ describe('ReactNative', () => {
expect(UIManager.createView.mock.calls.length).toBe(1);
expect(UIManager.setChildren.mock.calls.length).toBe(1);
expect(UIManager.manageChildren).not.toBeCalled();
expect(UIManager.updateView).toBeCalledWith(2, 'View', {foo: 'bar'});
expect(UIManager.updateView).toBeCalledWith(3, 'View', {foo: 'bar'});
});
it('should not call UIManager.updateView after render for properties that have not changed', () => {