Skip to content

Commit e4c9609

Browse files
committed
[RN] Add support for document instance in React Native
1 parent 37906d4 commit e4c9609

File tree

8 files changed

+81
-10
lines changed

8 files changed

+81
-10
lines changed

packages/react-native-renderer/src/ReactFabric.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,16 @@ import {
3838
getNodeFromInternalInstanceHandle,
3939
isChildPublicInstance,
4040
} from './ReactNativePublicCompat';
41-
import {getPublicInstanceFromInternalInstanceHandle} from './ReactFiberConfigFabric';
41+
import {
42+
getPublicInstanceFromInternalInstanceHandle,
43+
} from './ReactFiberConfigFabric';
4244

4345
// Module provided by RN:
44-
import {ReactFiberErrorDialog} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
46+
import {
47+
ReactFiberErrorDialog,
48+
createPublicRootInstance,
49+
type PublicRootInstance,
50+
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
4551
import {disableLegacyMode} from 'shared/ReactFeatureFlags';
4652

4753
if (typeof ReactFiberErrorDialog.showErrorDialog !== 'function') {
@@ -126,10 +132,16 @@ function render(
126132
onRecoverableError = options.onRecoverableError;
127133
}
128134

135+
const publicRootInstance = createPublicRootInstance(containerTag);
136+
const rootInstance = {
137+
publicInstance: publicRootInstance,
138+
containerTag,
139+
};
140+
129141
// TODO (bvaughn): If we decide to keep the wrapper component,
130142
// We could create a wrapper for containerTag as well to reduce special casing.
131143
root = createContainer(
132-
containerTag,
144+
rootInstance,
133145
concurrentRoot ? ConcurrentRoot : LegacyRoot,
134146
null,
135147
false,
@@ -140,6 +152,7 @@ function render(
140152
onRecoverableError,
141153
null,
142154
);
155+
143156
roots.set(containerTag, root);
144157
}
145158
updateContainer(element, root, null, callback);
@@ -155,6 +168,9 @@ function unmountComponentAtNode(containerTag: number) {
155168
function stopSurface(containerTag: number) {
156169
const root = roots.get(containerTag);
157170
if (root) {
171+
// Remove the reference to the public instance to prevent memory leaks.
172+
root.containerInfo.publicInstance = null;
173+
158174
// TODO: Is it safe to reset this now or should I wait since this unmount could be deferred?
159175
updateContainer(null, root, null, () => {
160176
roots.delete(containerTag);
@@ -170,6 +186,16 @@ function createPortal(
170186
return createPortalImpl(children, containerTag, null, key);
171187
}
172188

189+
function getPublicInstanceFromRootTag(
190+
rootTag: number,
191+
): PublicRootInstance | null {
192+
const root = roots.get(rootTag);
193+
if (root) {
194+
return root.containerInfo.publicInstance;
195+
}
196+
return null;
197+
}
198+
173199
setBatchingImplementation(batchedUpdatesImpl, discreteUpdates);
174200

175201
const roots = new Map<number, FiberRoot>();
@@ -195,6 +221,8 @@ export {
195221
// instance handles we use to dispatch events. This provides a way to access
196222
// the public instances we created from them (potentially created lazily).
197223
getPublicInstanceFromInternalInstanceHandle,
224+
// Returns the document instance for that root tag.
225+
getPublicInstanceFromRootTag,
198226
// DEV-only:
199227
isChildPublicInstance,
200228
};

packages/react-native-renderer/src/ReactFiberConfigFabric.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
createPublicTextInstance,
3232
type PublicInstance as ReactNativePublicInstance,
3333
type PublicTextInstance,
34+
type PublicRootInstance,
3435
} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
3536

3637
const {
@@ -108,7 +109,10 @@ export type TextInstance = {
108109
};
109110
export type HydratableInstance = Instance | TextInstance;
110111
export type PublicInstance = ReactNativePublicInstance;
111-
export type Container = number;
112+
export type Container = {
113+
containerTag: number,
114+
publicInstance: PublicRootInstance | null,
115+
};
112116
export type ChildSet = Object | Array<Node>;
113117
export type HostContext = $ReadOnly<{
114118
isInAParentText: boolean,
@@ -180,7 +184,7 @@ export function createInstance(
180184
const node = createNode(
181185
tag, // reactTag
182186
viewConfig.uiViewClassName, // viewName
183-
rootContainerInstance, // rootTag
187+
rootContainerInstance.containerTag, // rootTag
184188
updatePayload, // props
185189
internalInstanceHandle, // internalInstanceHandle
186190
);
@@ -189,6 +193,7 @@ export function createInstance(
189193
tag,
190194
viewConfig,
191195
internalInstanceHandle,
196+
rootContainerInstance.publicInstance,
192197
);
193198

194199
return {
@@ -221,7 +226,7 @@ export function createTextInstance(
221226
const node = createNode(
222227
tag, // reactTag
223228
'RCTRawText', // viewName
224-
rootContainerInstance, // rootTag
229+
rootContainerInstance.containerTag, // rootTag
225230
{text: text}, // props
226231
internalInstanceHandle, // instance handle
227232
);
@@ -501,7 +506,7 @@ export function finalizeContainerChildren(
501506
newChildren: ChildSet,
502507
): void {
503508
if (!enableFabricCompleteRootInCommitPhase) {
504-
completeRoot(container, newChildren);
509+
completeRoot(container.containerTag, newChildren);
505510
}
506511
}
507512

@@ -511,7 +516,7 @@ export function replaceContainerChildren(
511516
): void {
512517
// Noop - children will be replaced in finalizeContainerChildren
513518
if (enableFabricCompleteRootInCommitPhase) {
514-
completeRoot(container, newChildren);
519+
completeRoot(container.containerTag, newChildren);
515520
}
516521
}
517522

packages/react-native-renderer/src/ReactNativeRenderer.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {ReactPortal, ReactNodeList} from 'shared/ReactTypes';
1111
import type {ElementRef, ElementType, MixedElement} from 'react';
1212
import type {FiberRoot} from 'react-reconciler/src/ReactInternalTypes';
1313
import type {RenderRootOptions} from './ReactNativeTypes';
14+
import type {Container} from 'react-reconciler/src/ReactFiberConfig';
1415

1516
import './ReactNativeInjection';
1617

@@ -143,10 +144,16 @@ function render(
143144
onRecoverableError = options.onRecoverableError;
144145
}
145146

147+
const rootInstance: Container = {
148+
containerTag,
149+
// $FlowExpectedError[incompatible-type] the legacy renderer does not use public root instances
150+
publicInstance: null,
151+
};
152+
146153
// TODO (bvaughn): If we decide to keep the wrapper component,
147154
// We could create a wrapper for containerTag as well to reduce special casing.
148155
root = createContainer(
149-
containerTag,
156+
rootInstance,
150157
LegacyRoot,
151158
null,
152159
false,

packages/react-native-renderer/src/ReactNativeTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ export opaque type Node = mixed;
231231
export opaque type InternalInstanceHandle = mixed;
232232
type PublicInstance = mixed;
233233
type PublicTextInstance = mixed;
234+
export opaque type PublicRootInstance = mixed;
234235

235236
export type ReactFabricType = {
236237
findHostInstance_DEPRECATED<TElementType: ElementType>(

packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
export opaque type PublicInstance = mixed;
1111
export opaque type PublicTextInstance = mixed;
12+
export opaque type PublicRootInstance = mixed;
1213

1314
module.exports = {
1415
get BatchedBridge() {
@@ -59,4 +60,7 @@ module.exports = {
5960
get createPublicTextInstance() {
6061
return require('./createPublicTextInstance').default;
6162
},
63+
get createPublicRootInstance() {
64+
return require('./createPublicRootInstance').default;
65+
},
6266
};

packages/react-native-renderer/src/__mocks__/react-native/Libraries/ReactPrivate/createPublicInstance.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,20 @@
77
* @flow strict
88
*/
99

10-
import type {PublicInstance} from './ReactNativePrivateInterface';
10+
import type {
11+
PublicInstance,
12+
PublicRootInstance,
13+
} from './ReactNativePrivateInterface';
1114

1215
export default function createPublicInstance(
1316
tag: number,
1417
viewConfig: mixed,
1518
internalInstanceHandle: mixed,
19+
rootPublicInstance: PublicRootInstance | null,
1620
): PublicInstance {
1721
return {
1822
__nativeTag: tag,
1923
__internalInstanceHandle: internalInstanceHandle,
24+
__rootPublicInstance: rootPublicInstance,
2025
};
2126
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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 strict
8+
*/
9+
10+
import type {PublicRootInstance} from './ReactNativePrivateInterface';
11+
12+
export default function createPublicRootInstance(
13+
rootTag: number,
14+
): PublicRootInstance {
15+
return null;
16+
}

scripts/flow/react-native-host-hooks.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'
143143
};
144144
declare export opaque type PublicInstance;
145145
declare export opaque type PublicTextInstance;
146+
declare export opaque type PublicRootInstance;
146147
declare export function getNodeFromPublicInstance(
147148
publicInstance: PublicInstance,
148149
): Object;
@@ -153,7 +154,11 @@ declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'
153154
tag: number,
154155
viewConfig: __ViewConfig,
155156
internalInstanceHandle: mixed,
157+
publicRootInstance: PublicRootInstance | null,
156158
): PublicInstance;
159+
declare export function createPublicRootInstance(
160+
rootTag: number,
161+
): PublicRootInstance;
157162
declare export function createPublicTextInstance(
158163
internalInstanceHandle: mixed,
159164
): PublicTextInstance;

0 commit comments

Comments
 (0)