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.
Sebastian Markbåge committed
Apr 9, 2018 at 18:41 UTC
2f7bca0eb2487955e71a45e288e5847b5af522a5
6 files changed
+29
-65
packages/react-native-renderer/src/ReactFabricRenderer.js
+11
-3
@@ -20,7 +20,6 @@ import * as ReactNativeAttributePayload from './ReactNativeAttributePayload';
20
import * as ReactNativeFrameScheduling from './ReactNativeFrameScheduling';
21
import * as ReactNativeViewConfigRegistry from './ReactNativeViewConfigRegistry';
22
import ReactFiberReconciler from 'react-reconciler';
23
-import ReactNativeTagHandles from './ReactNativeTagHandles';
23
24
import deepFreezeAndThrowOnMutationInDev from 'deepFreezeAndThrowOnMutationInDev';
25
import emptyObject from 'fbjs/lib/emptyObject';
@@ -30,6 +29,12 @@ import TextInputState from 'TextInputState';
29
import FabricUIManager from 'FabricUIManager';
30
import UIManager from 'UIManager';
31
32
+// Counter for uniquely identifying views.
33
+// % 10 === 1 means it is a rootTag.
34
+// % 2 === 0 means it is a Fabric tag.
35
+// This means that they never overlap.
36
+let nextReactTag = 2;
37
+
38
/**
39
* This is used for refs on host components.
40
*/
@@ -133,7 +138,9 @@ const ReactFabricRenderer = ReactFiberReconciler({
138
hostContext: {},
139
internalInstanceHandle: Object,
140
): Instance {
136
- const tag = ReactNativeTagHandles.allocateTag();
141
+ const tag = nextReactTag;
142
+ nextReactTag += 2;
143
+
144
const viewConfig = ReactNativeViewConfigRegistry.get(type);
145
146
if (__DEV__) {
@@ -171,7 +178,8 @@ const ReactFabricRenderer = ReactFiberReconciler({
178
hostContext: {},
179
internalInstanceHandle: Object,
180
): TextInstance {
174
- const tag = ReactNativeTagHandles.allocateTag();
181
+ const tag = nextReactTag;
182
+ nextReactTag += 2;
183
184
const node = FabricUIManager.createNode(
185
tag, // reactTag
packages/react-native-renderer/src/ReactNativeEventEmitter.js
+1
-2
@@ -13,7 +13,6 @@ import {batchedUpdates} from 'events/ReactGenericBatching';
13
import warning from 'fbjs/lib/warning';
14
15
import {getInstanceFromNode} from './ReactNativeComponentTree';
16
-import ReactNativeTagHandles from './ReactNativeTagHandles';
16
17
import type {AnyNativeEvent} from 'events/PluginModuleType';
18
@@ -166,7 +165,7 @@ export function receiveTouches(
165
let rootNodeID = null;
166
const target = nativeEvent.target;
167
if (target !== null && target !== undefined) {
169
- if (target < ReactNativeTagHandles.tagsStartAt) {
168
+ if (target < 1) {
169
if (__DEV__) {
170
warning(
171
false,
packages/react-native-renderer/src/ReactNativeFiberRenderer.js
+15
-3
@@ -25,7 +25,6 @@ import {
25
} from './ReactNativeComponentTree';
26
import ReactNativeFiberHostComponent from './ReactNativeFiberHostComponent';
27
import * as ReactNativeFrameScheduling from './ReactNativeFrameScheduling';
28
-import ReactNativeTagHandles from './ReactNativeTagHandles';
28
29
type Container = number;
30
export type Instance = {
@@ -36,6 +35,19 @@ export type Instance = {
35
type Props = Object;
36
type TextInstance = number;
37
38
+// Counter for uniquely identifying views.
39
+// % 10 === 1 means it is a rootTag.
40
+// % 2 === 0 means it is a Fabric tag.
41
+let nextReactTag = 3;
42
+function allocateTag() {
43
+ let tag = nextReactTag;
44
+ if (tag % 10 === 1) {
45
+ tag += 2;
46
+ }
47
+ nextReactTag = tag + 2;
48
+ return tag;
49
+}
50
+
51
function recursivelyUncacheFiberNode(node: Instance | TextInstance) {
52
if (typeof node === 'number') {
53
// Leaf node (eg text)
@@ -62,7 +74,7 @@ const NativeRenderer = ReactFiberReconciler({
74
hostContext: {},
75
internalInstanceHandle: Object,
76
): Instance {
65
- const tag = ReactNativeTagHandles.allocateTag();
77
+ const tag = allocateTag();
78
const viewConfig = ReactNativeViewConfigRegistry.get(type);
79
80
if (__DEV__) {
@@ -101,7 +113,7 @@ const NativeRenderer = ReactFiberReconciler({
113
hostContext: {},
114
internalInstanceHandle: Object,
115
): TextInstance {
104
- const tag = ReactNativeTagHandles.allocateTag();
116
+ const tag = allocateTag();
117
118
UIManager.createView(
119
tag, // reactTag
packages/react-native-renderer/src/ReactNativeTagHandles.js
deleted
-54
@@ -1,54 +0,0 @@
1
-/**
2
- * Copyright (c) 2015-present, Facebook, Inc.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- * @flow
8
- */
9
-
10
-import invariant from 'fbjs/lib/invariant';
11
-
12
-/**
13
- * Keeps track of allocating and associating native "tags" which are numeric,
14
- * unique view IDs. All the native tags are negative numbers, to avoid
15
- * collisions, but in the JS we keep track of them as positive integers to store
16
- * them effectively in Arrays. So we must refer to them as "inverses" of the
17
- * native tags (that are * normally negative).
18
- *
19
- * It *must* be the case that every `rootNodeID` always maps to the exact same
20
- * `tag` forever. The easiest way to accomplish this is to never delete
21
- * anything from this table.
22
- * Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to
23
- * unmount a component with a `rootNodeID`, then mount a new one in its place,
24
- */
25
-const INITIAL_TAG_COUNT = 1;
26
-const ReactNativeTagHandles = {
27
- tagsStartAt: INITIAL_TAG_COUNT,
28
- tagCount: INITIAL_TAG_COUNT,
29
-
30
- allocateTag: function(): number {
31
- // Skip over root IDs as those are reserved for native
32
- while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) {
33
- ReactNativeTagHandles.tagCount++;
34
- }
35
- const tag = ReactNativeTagHandles.tagCount;
36
- ReactNativeTagHandles.tagCount++;
37
- return tag;
38
- },
39
-
40
- assertRootTag: function(tag: number): void {
41
- invariant(
42
- this.reactTagIsNativeTopRootID(tag),
43
- 'Expect a native root tag, instead got %s',
44
- tag,
45
- );
46
- },
47
-
48
- reactTagIsNativeTopRootID: function(reactTag: number): boolean {
49
- // We reserve all tags that are 1 mod 10 for native root views
50
- return reactTag % 10 === 1;
51
- },
52
-};
53
-
54
-export default ReactNativeTagHandles;
packages/react-native-renderer/src/__mocks__/UIManager.js
+1
-2
@@ -9,7 +9,6 @@
9
10
// Mock of the Native Hooks
11
12
-const ReactNativeTagHandles = require('../ReactNativeTagHandles').default;
12
const invariant = require('fbjs/lib/invariant');
13
14
// Map of viewTag -> {children: [childTag], parent: ?parentTag}
@@ -18,7 +17,7 @@ let views = new Map();
17
18
function autoCreateRoot(tag) {
19
// Seriously, this is how we distinguish roots in RN.
21
- if (!views.has(tag) && ReactNativeTagHandles.reactTagIsNativeTopRootID(tag)) {
20
+ if (!views.has(tag) && tag % 10 === 1) {
21
roots.push(tag);
22
views.set(tag, {
23
children: [],
packages/react-native-renderer/src/__tests__/ReactNativeMount-test.internal.js
+1
-1
@@ -57,7 +57,7 @@ describe('ReactNative', () => {
57
expect(UIManager.createView.mock.calls.length).toBe(1);
58
expect(UIManager.setChildren.mock.calls.length).toBe(1);
59
expect(UIManager.manageChildren).not.toBeCalled();
60
- expect(UIManager.updateView).toBeCalledWith(2, 'View', {foo: 'bar'});
60
+ expect(UIManager.updateView).toBeCalledWith(3, 'View', {foo: 'bar'});
61
});
62
63
it('should not call UIManager.updateView after render for properties that have not changed', () => {