@samitouri / QOS-React / commits / 2f7bca0eb2

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', () => {