Skip to content

Commit 108aed0

Browse files
authored
Fix use of stale props in Fabric events (#26408)
## Summary We had to revert the last React sync to React Native because we saw issues with Responder events using stale event handlers instead of recent versions. I reviewed the merged PRs and realized the problem was in the refactor I did in #26321. In that PR, we moved `currentProps` from `canonical`, which is a singleton referenced by all versions of the same fiber, to the fiber itself. This is causing the staleness we observed in events. This PR does a partial revert of the refactor in #26321, bringing back the `canonical` object but moving `publicInstance` to one of its fields, instead of being the `canonical` object itself. ## How did you test this change? Existing unit tests continue working (I didn't manage to get a repro using the test renderer). I manually tested this change in Meta infra and saw the problem was fixed.
1 parent 8fa41ff commit 108aed0

File tree

6 files changed

+47
-41
lines changed

6 files changed

+47
-41
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ import {getPublicInstance} from './ReactFabricHostConfig';
2222
function getInstanceFromNode(node: Instance | TextInstance): Fiber | null {
2323
const instance: Instance = (node: $FlowFixMe); // In React Native, node is never a text instance
2424

25-
if (instance.internalInstanceHandle != null) {
26-
return instance.internalInstanceHandle;
25+
if (
26+
instance.canonical != null &&
27+
instance.canonical.internalInstanceHandle != null
28+
) {
29+
return instance.canonical.internalInstanceHandle;
2730
}
2831

2932
// $FlowFixMe[incompatible-return] DevTools incorrectly passes a fiber in React Native.
@@ -41,7 +44,7 @@ function getNodeFromInstance(fiber: Fiber): PublicInstance {
4144
}
4245

4346
function getFiberCurrentPropsFromNode(instance: Instance): Props {
44-
return instance.currentProps;
47+
return instance.canonical.currentProps;
4548
}
4649

4750
export {

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

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,15 @@ export type Props = Object;
5555
export type Instance = {
5656
// Reference to the shadow node.
5757
node: Node,
58-
nativeTag: number,
59-
viewConfig: ViewConfig,
60-
currentProps: Props,
61-
// Reference to the React handle (the fiber)
62-
internalInstanceHandle: Object,
63-
// Exposed through refs.
64-
publicInstance: ReactFabricHostComponent,
58+
canonical: {
59+
nativeTag: number,
60+
viewConfig: ViewConfig,
61+
currentProps: Props,
62+
// Reference to the React handle (the fiber)
63+
internalInstanceHandle: Object,
64+
// Exposed through refs.
65+
publicInstance: ReactFabricHostComponent,
66+
},
6567
};
6668
export type TextInstance = {node: Node, ...};
6769
export type HydratableInstance = Instance | TextInstance;
@@ -148,11 +150,13 @@ export function createInstance(
148150

149151
return {
150152
node: node,
151-
nativeTag: tag,
152-
viewConfig,
153-
currentProps: props,
154-
internalInstanceHandle,
155-
publicInstance: component,
153+
canonical: {
154+
nativeTag: tag,
155+
viewConfig,
156+
currentProps: props,
157+
internalInstanceHandle,
158+
publicInstance: component,
159+
},
156160
};
157161
}
158162

@@ -222,8 +226,8 @@ export function getChildHostContext(
222226
}
223227

224228
export function getPublicInstance(instance: Instance): null | PublicInstance {
225-
if (instance.publicInstance != null) {
226-
return instance.publicInstance;
229+
if (instance.canonical != null && instance.canonical.publicInstance != null) {
230+
return instance.canonical.publicInstance;
227231
}
228232

229233
// For compatibility with the legacy renderer, in case it's used with Fabric
@@ -249,12 +253,12 @@ export function prepareUpdate(
249253
newProps: Props,
250254
hostContext: HostContext,
251255
): null | Object {
252-
const viewConfig = instance.viewConfig;
256+
const viewConfig = instance.canonical.viewConfig;
253257
const updatePayload = diff(oldProps, newProps, viewConfig.validAttributes);
254258
// TODO: If the event handlers have changed, we need to update the current props
255259
// in the commit phase but there is no host config hook to do it yet.
256260
// So instead we hack it by updating it in the render phase.
257-
instance.currentProps = newProps;
261+
instance.canonical.currentProps = newProps;
258262
return updatePayload;
259263
}
260264

@@ -333,11 +337,7 @@ export function cloneInstance(
333337
}
334338
return {
335339
node: clone,
336-
nativeTag: instance.nativeTag,
337-
viewConfig: instance.viewConfig,
338-
currentProps: instance.currentProps,
339-
internalInstanceHandle: instance.internalInstanceHandle,
340-
publicInstance: instance.publicInstance,
340+
canonical: instance.canonical,
341341
};
342342
}
343343

@@ -347,19 +347,15 @@ export function cloneHiddenInstance(
347347
props: Props,
348348
internalInstanceHandle: Object,
349349
): Instance {
350-
const viewConfig = instance.viewConfig;
350+
const viewConfig = instance.canonical.viewConfig;
351351
const node = instance.node;
352352
const updatePayload = create(
353353
{style: {display: 'none'}},
354354
viewConfig.validAttributes,
355355
);
356356
return {
357357
node: cloneNodeWithNewProps(node, updatePayload),
358-
nativeTag: instance.nativeTag,
359-
viewConfig: instance.viewConfig,
360-
currentProps: instance.currentProps,
361-
internalInstanceHandle: instance.internalInstanceHandle,
362-
publicInstance: instance.publicInstance,
358+
canonical: instance.canonical,
363359
};
364360
}
365361

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ function getInstanceFromTag(tag) {
2424
function getTagFromInstance(inst) {
2525
let nativeInstance = inst.stateNode;
2626
let tag = nativeInstance._nativeTag;
27-
if (tag === undefined) {
27+
if (tag === undefined && nativeInstance.canonical != null) {
2828
// For compatibility with Fabric
29-
tag = nativeInstance.nativeTag;
30-
nativeInstance = nativeInstance.publicInstance;
29+
tag = nativeInstance.canonical.nativeTag;
30+
nativeInstance = nativeInstance.canonical.publicInstance;
3131
}
3232

3333
if (!tag) {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,11 @@ function getInspectorDataForViewAtPoint(
223223
}
224224

225225
closestInstance =
226-
internalInstanceHandle.stateNode.internalInstanceHandle;
226+
internalInstanceHandle.stateNode.canonical.internalInstanceHandle;
227227

228228
// Note: this is deprecated and we want to remove it ASAP. Keeping it here for React DevTools compatibility for now.
229-
const nativeViewTag = internalInstanceHandle.stateNode.nativeTag;
229+
const nativeViewTag =
230+
internalInstanceHandle.stateNode.canonical.nativeTag;
230231

231232
nativeFabricUIManager.measure(
232233
node,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ export function getChildHostContext(
218218

219219
export function getPublicInstance(instance: Instance): * {
220220
// $FlowExpectedError[prop-missing] For compatibility with Fabric
221-
if (instance.publicInstance != null) {
222-
return instance.publicInstance;
221+
if (instance.canonical != null && instance.canonical.publicInstance != null) {
222+
return instance.canonical.publicInstance;
223223
}
224224

225225
return instance;

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,12 @@ export function findHostInstance_DEPRECATED<TElementType: ElementType>(
5656
}
5757

5858
// For compatibility with Fabric instances
59-
if (componentOrHandle.publicInstance) {
59+
if (
60+
componentOrHandle.canonical &&
61+
componentOrHandle.canonical.publicInstance
62+
) {
6063
// $FlowExpectedError[incompatible-return] Can't refine componentOrHandle as a Fabric instance
61-
return componentOrHandle.publicInstance;
64+
return componentOrHandle.canonical.publicInstance;
6265
}
6366

6467
// For compatibility with legacy renderer instances
@@ -117,8 +120,11 @@ export function findNodeHandle(componentOrHandle: any): ?number {
117120
}
118121

119122
// For compatibility with Fabric instances
120-
if (componentOrHandle.nativeTag != null) {
121-
return componentOrHandle.nativeTag;
123+
if (
124+
componentOrHandle.canonical != null &&
125+
componentOrHandle.canonical.nativeTag != null
126+
) {
127+
return componentOrHandle.canonical.nativeTag;
122128
}
123129

124130
// For compatibility with Fabric public instances

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy