fix: add missing `target` property in `LayoutChangeEvent`
Summary:
According to documentation the target property exist:
Also if you log nativeEvent you'll see that the property indeed exist, however from TS declaration perspective this property doesn't exist.
In this PR I'm adding it at according to RN documentation types.
Changelog:
- [General] [Fixed] - add missing target property in LayoutChangeEvent
Test Plan:
| Before | After |
|---|---|
Hi @kirillzyusko!
Thank you for your pull request and welcome to our community.
Action Required
In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.
Process
In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.
Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.
If you have received this in error or have any questions, please contact us at [email protected]. Thanks!
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!
@NickGerleman sorry to tag you, but I've seen you recently worked on reviewing TS mismatches PR - may I kindly ask you to review this PR?
@cipolleschi sorry to tag you, but you are the only one person from Meta with whom I spoke/chatted and who was responsive 😊
Maybe you know a correct person who can review these changes? 👀
@cipolleschi has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.
From the quick glance, I think this is supposed to instead derive from TargetedEvent where target is also not nullable.
https://github.com/facebook/react-native/blob/659712b475025bda1a534bf3db094e77411b2014/packages/react-native/Libraries/Types/CoreEventTypes.d.ts#L249
@NickGerleman I took specification from website:
Do you think it should be like this?
export type LayoutChangeEvent = NativeSyntheticEvent<{
layout: LayoutRectangle;
}> & Nullable<TargetedEvent>;
Hmm it looks like both the website, and existing TS types aren’t correct.
https://github.com/facebook/react-native/blob/e1b0914e094a7fd119e54575c5c0ff06008ada22/packages/react-native/Libraries/Types/CoreEventTypes.js#L31
So, it can be nullable, can be used by default in more places, and can also be a ref.
@NickGerleman so would it make sense to make changes to TargetedEvent type and then use union for LayoutChangeEvent?
I'm happy to make these changes and submit a PR, but just want to understand what you'd like to see in that PR 👀
I think the best thing here, if possible, would be to change the TS definitions, to match Flow organization.
@NickGerleman I took a look on flow definitions and it seems like there is also a problem. This declares target property that belongs to event
https://github.com/facebook/react-native/blob/e1b0914e094a7fd119e54575c5c0ff06008ada22/packages/react-native/Libraries/Types/CoreEventTypes.js#L31
And we have this property in TS definition too:
https://github.com/facebook/react-native/blob/e1b0914e094a7fd119e54575c5c0ff06008ada22/packages/react-native/Libraries/Types/CoreEventTypes.d.ts#L44
But this PR adds target property to nativeEvent. This property really exist and it's number - I added this code to onLayout:
console.log(e.target);
console.log(e.nativeEvent);
And output will be:
LOG {"_children": [{"_children": [Array], "_internalFiberInstanceHandleDEV": [FiberNode], "_nativeTag": 515, "viewConfig": [Object]}], "_internalFiberInstanceHandleDEV": {"_debugHookTypes": null, "_debugNeedsRemount": false, "_debugOwner": {"_debugHookTypes": null, "_debugNeedsRemount": false, "_debugOwner": [FiberNode], "_debugSource": undefined, "actualDuration": 10.847953051328659, "actualStartTime": 69964979.916083, "alternate": null, "child": [Circular], "childLanes": 0, "deletions": null, "dependencies": null, "elementType": [Function ScrollView], "flags": 5, "index": 0, "key": null, "lanes": 0, "memoizedProps": [Object], "memoizedState": [Object], "mode": 2, "pendingProps": [Object], "ref": null, "return": [FiberNode], "selfBaseDuration": 0.11020900309085846, "sibling": null, "stateNode": [ScrollView], "subtreeFlags": 9439749, "tag": 1, "treeBaseDuration": 6.337453976273537, "type": [Function ScrollView], "updateQueue": [Object]}, "_debugSource": undefined, "actualDuration": 10.736160054802895, "actualStartTime": 69964980.027458, "alternate": null, "child": {"_debugHookTypes": null, "_debugNeedsRemount": false, "_debugOwner": [FiberNode], "_debugSource": undefined, "actualDuration": 10.5658690482378, "actualStartTime": 69964980.037125, "alternate": null, "child": [FiberNode], "childLanes": 0, "deletions": null, "dependencies": null, "elementType": "RCTScrollContentView", "flags": 512, "index": 0, "key": null, "lanes": 0, "memoizedProps": [Object], "memoizedState": null, "mode": 2, "pendingProps": [Object], "ref": [Function anonymous], "return": [Circular], "selfBaseDuration": 0.007250010967254639, "sibling": null, "stateNode": [ReactNativeFiberHostComponent], "subtreeFlags": 9439749, "tag": 5, "treeBaseDuration": 6.218203976750374, "type": "RCTScrollContentView", "updateQueue": null}, "childLanes": 0, "deletions": null, "dependencies": null, "elementType": "RCTScrollView", "flags": 512, "index": 0, "key": null, "lanes": 0, "memoizedProps": {"alwaysBounceHorizontal": undefined, "alwaysBounceVertical": true, "children": <RCTScrollContentView … />, "collapsable": false, "contentContainerStyle": [Object], "forwardedRef": [Function anonymous], "onContentSizeChange": null, "onLayout": [Function anonymous], "onMomentumScrollBegin": [Function anonymous], "onMomentumScrollEnd": [Function anonymous], "onResponderGrant": [Function anonymous], "onResponderReject": [Function anonymous], "onResponderRelease": [Function anonymous], "onResponderTerminationRequest": [Function anonymous], "onScroll": [Function anonymous], "onScrollBeginDrag": [Function anonymous], "onScrollEndDrag": [Function anonymous], "onScrollShouldSetResponder": [Function anonymous], "onStartShouldSetResponder": [Function anonymous], "onStartShouldSetResponderCapture": [Function anonymous], "onTouchCancel": [Function anonymous], "onTouchEnd": [Function anonymous], "onTouchMove": [Function anonymous], "onTouchStart": [Function anonymous], "pagingEnabled": false, "scrollEventThrottle": 16, "scrollViewRef": [Function forwardRef], "sendMomentumEvents": false, "snapToEnd": true, "snapToStart": true, "style": [Array], "testID": "aware_scroll_view_container"}, "memoizedState": null, "mode": 2, "pendingProps": {"alwaysBounceHorizontal": undefined, "alwaysBounceVertical": true, "children": <RCTScrollContentView … />, "collapsable": false, "contentContainerStyle": [Object], "forwardedRef": [Function anonymous], "onContentSizeChange": null, "onLayout": [Function anonymous], "onMomentumScrollBegin": [Function anonymous], "onMomentumScrollEnd": [Function anonymous], "onResponderGrant": [Function anonymous], "onResponderReject": [Function anonymous], "onResponderRelease": [Function anonymous], "onResponderTerminationRequest": [Function anonymous], "onScroll": [Function anonymous], "onScrollBeginDrag": [Function anonymous], "onScrollEndDrag": [Function anonymous], "onScrollShouldSetResponder": [Function anonymous], "onStartShouldSetResponder": [Function anonymous], "onStartShouldSetResponderCapture": [Function anonymous], "onTouchCancel": [Function anonymous], "onTouchEnd": [Function anonymous], "onTouchMove": [Function anonymous], "onTouchStart": [Function anonymous], "pagingEnabled": false, "scrollEventThrottle": 16, "scrollViewRef": [Function forwardRef], "sendMomentumEvents": false, "snapToEnd": true, "snapToStart": true, "style": [Array], "testID": "aware_scroll_view_container"}, "ref": [Function anonymous], "return": {"_debugHookTypes": null, "_debugNeedsRemount": false, "_debugOwner": [FiberNode], "_debugSource": undefined, "actualDuration": 10.847953051328659, "actualStartTime": 69964979.916083, "alternate": null, "child": [Circular], "childLanes": 0, "deletions": null, "dependencies": null, "elementType": [Function ScrollView], "flags": 5, "index": 0, "key": null, "lanes": 0, "memoizedProps": [Object], "memoizedState": [Object], "mode": 2, "pendingProps": [Object], "ref": null, "return": [FiberNode], "selfBaseDuration": 0.11020900309085846, "sibling": null, "stateNode": [ScrollView], "subtreeFlags": 9439749, "tag": 1, "treeBaseDuration": 6.337453976273537, "type": [Function ScrollView], "updateQueue": [Object]}, "selfBaseDuration": 0.009040996432304382, "sibling": null, "stateNode": [Circular], "subtreeFlags": 9439749, "tag": 5, "treeBaseDuration": 6.227244973182678, "type": "RCTScrollView", "updateQueue": null}, "_nativeTag": 517, "flashScrollIndicators": [Function anonymous], "getInnerViewNode": [Function anonymous], "getInnerViewRef": [Function anonymous], "getNativeScrollRef": [Function anonymous], "getScrollResponder": [Function anonymous], "getScrollableNode": [Function anonymous], "scrollResponderScrollNativeHandleToKeyboard": [Function anonymous], "scrollResponderZoomTo": [Function anonymous], "scrollTo": [Function anonymous], "scrollToEnd": [Function anonymous], "viewConfig": {"Commands": {"flashScrollIndicators": 4, "getConstants": 5, "getContentSize": 0, "scrollTo": 1, "scrollToEnd": 2, "zoomToRect": 3}, "Constants": {}, "Manager": "ScrollViewManager", "NativeProps": {"alwaysBounceHorizontal": "BOOL", "alwaysBounceVertical": "BOOL", "automaticallyAdjustContentInsets": "BOOL", "automaticallyAdjustKeyboardInsets": "BOOL", "automaticallyAdjustsScrollIndicatorInsets": "BOOL", "bounces": "BOOL", "bouncesZoom": "BOOL", "canCancelContentTouches": "BOOL", "centerContent": "BOOL", "contentInset": "UIEdgeInsets", "contentInsetAdjustmentBehavior": "UIScrollViewContentInsetAdjustmentBehavior", "contentOffset": "CGPoint", "decelerationRate": "CGFloat", "directionalLockEnabled": "BOOL", "disableIntervalMomentum": "BOOL", "indicatorStyle": "UIScrollViewIndicatorStyle", "inverted": "BOOL", "keyboardDismissMode": "UIScrollViewKeyboardDismissMode", "maintainVisibleContentPosition": "NSDictionary", "maximumZoomScale": "CGFloat", "minimumZoomScale": "CGFloat", "onMomentumScrollBegin": "BOOL", "onMomentumScrollEnd": "BOOL", "onScroll": "BOOL", "onScrollBeginDrag": "BOOL", "onScrollEndDrag": "BOOL", "onScrollToTop": "BOOL", "overflow": "YGOverflow", "pagingEnabled": "BOOL", "pinchGestureEnabled": "BOOL", "scrollEnabled": "BOOL", "scrollEventThrottle": "NSTimeInterval", "scrollIndicatorInsets": "UIEdgeInsets", "scrollToOverflowEnabled": "BOOL", "scrollsToTop": "BOOL", "showsHorizontalScrollIndicator": "BOOL", "showsVerticalScrollIndicator": "BOOL", "snapToAlignment": "NSString", "snapToEnd": "BOOL", "snapToInterval": "int", "snapToOffsets": "NSArray<NSNumber *>", "snapToStart": "BOOL", "zoomScale": "CGFloat"}, "baseModuleName": "RCTView", "bubblingEventTypes": {"topBlur": [Object], "topChange": [Object], "topClick": [Object], "topEndEditing": [Object], "topFocus": [Object], "topGotPointerCapture": [Object], "topKeyPress": [Object], "topLostPointerCapture": [Object], "topPointerCancel": [Object], "topPointerDown": [Object], "topPointerEnter": [Object], "topPointerLeave": [Object], "topPointerMove": [Object], "topPointerOut": [Object], "topPointerOver": [Object], "topPointerUp": [Object], "topPress": [Object], "topSubmitEditing": [Object], "topTouchCancel": [Object], "topTouchEnd": [Object], "topTouchMove": [Object], "topTouchStart": [Object]}, "directEventTypes": {"onGestureHandlerEvent": [Object], "onGestureHandlerStateChange": [Object], "topAccessibilityAction": [Object], "topAccessibilityEscape": [Object], "topAccessibilityTap": [Object], "topLayout": [Object], "topMagicTap": [Object], "topMomentumScrollBegin": [Object], "topMomentumScrollEnd": [Object], "topScroll": [Object], "topScrollBeginDrag": [Object], "topScrollEndDrag": [Object], "topScrollToTop": [Object]}, "uiViewClassName": "RCTScrollView", "validAttributes": {"accessibilityActions": true, "accessibilityElementsHidden": true, "accessibilityHint": true, "accessibilityIgnoresInvertColors": true, "accessibilityLabel": true, "accessibilityLanguage": true, "accessibilityRole": true, "accessibilityState": true, "accessibilityValue": true, "accessibilityViewIsModal": true, "accessible": true, "alignContent": true, "alignItems": true, "alignSelf": true, "alwaysBounceHorizontal": true, "alwaysBounceVertical": true, "aspectRatio": true, "automaticallyAdjustContentInsets": true, "automaticallyAdjustKeyboardInsets": true, "automaticallyAdjustsScrollIndicatorInsets": true, "backfaceVisibility": true, "backgroundColor": [Object], "borderBlockColor": [Object], "borderBlockEndColor": [Object], "borderBlockEndWidth": true, "borderBlockStartColor": [Object], "borderBlockStartWidth": true, "borderBlockWidth": true, "borderBottomColor": [Object], "borderBottomEndRadius": true, "borderBottomLeftRadius": true, "borderBottomRightRadius": true, "borderBottomStartRadius": true, "borderBottomWidth": true, "borderColor": [Object], "borderCurve": true, "borderEndColor": [Object], "borderEndEndRadius": true, "borderEndStartRadius": true, "borderEndWidth": true, "borderLeftColor": [Object], "borderLeftWidth": true, "borderRadius": true, "borderRightColor": [Object], "borderRightWidth": true, "borderStartColor": [Object], "borderStartEndRadius": true, "borderStartStartRadius": true, "borderStartWidth": true, "borderStyle": true, "borderTopColor": [Object], "borderTopEndRadius": true, "borderTopLeftRadius": true, "borderTopRightRadius": true, "borderTopStartRadius": true, "borderTopWidth": true, "borderWidth": true, "bottom": true, "bounces": true, "bouncesZoom": true, "canCancelContentTouches": true, "centerContent": true, "collapsable": true, "columnGap": true, "contentInset": [Object], "contentInsetAdjustmentBehavior": true, "contentOffset": [Object], "decelerationRate": true, "direction": true, "directionalLockEnabled": true, "disableIntervalMomentum": true, "display": true, "end": true, "experimental_layoutConformance": true, "flex": true, "flexBasis": true, "flexDirection": true, "flexGrow": true, "flexShrink": true, "flexWrap": true, "gap": true, "height": true, "hitSlop": [Object], "indicatorStyle": true, "inverted": true, "justifyContent": true, "keyboardDismissMode": true, "left": true, "maintainVisibleContentPosition": true, "margin": true, "marginBottom": true, "marginEnd": true, "marginHorizontal": true, "marginLeft": true, "marginRight": true, "marginStart": true, "marginTop": true, "marginVertical": true, "maxHeight": true, "maxWidth": true, "maximumZoomScale": true, "minHeight": true, "minWidth": true, "minimumZoomScale": true, "nativeID": true, "needsOffscreenAlphaCompositing": true, "onAccessibilityAction": true, "onAccessibilityEscape": true, "onAccessibilityTap": true, "onClick": true, "onGotPointerCapture": true, "onLayout": true, "onLostPointerCapture": true, "onMagicTap": true, "onMomentumScrollBegin": true, "onMomentumScrollEnd": true, "onMoveShouldSetResponder": true, "onMoveShouldSetResponderCapture": true, "onPointerCancel": true, "onPointerDown": true, "onPointerEnter": true, "onPointerLeave": true, "onPointerMove": true, "onPointerOut": true, "onPointerOver": true, "onPointerUp": true, "onResponderEnd": true, "onResponderGrant": true, "onResponderMove": true, "onResponderReject": true, "onResponderRelease": true, "onResponderStart": true, "onResponderTerminate": true, "onResponderTerminationRequest": true, "onScroll": true, "onScrollBeginDrag": true, "onScrollEndDrag": true, "onScrollToTop": true, "onShouldBlockNativeResponder": true, "onStartShouldSetResponder": true, "onStartShouldSetResponderCapture": true, "onTouchCancel": true, "onTouchEnd": true, "onTouchMove": true, "onTouchStart": true, "opacity": true, "overflow": true, "padding": true, "paddingBottom": true, "paddingEnd": true, "paddingHorizontal": true, "paddingLeft": true, "paddingRight": true, "paddingStart": true, "paddingTop": true, "paddingVertical": true, "pagingEnabled": true, "pinchGestureEnabled": true, "pointerEvents": true, "position": true, "removeClippedSubviews": true, "right": true, "role": true, "rowGap": true, "scrollEnabled": true, "scrollEventThrottle": true, "scrollIndicatorInsets": [Object], "scrollToOverflowEnabled": true, "scrollsToTop": true, "shadowColor": [Object], "shadowOffset": [Object], "shadowOpacity": true, "shadowRadius": true, "shouldRasterizeIOS": true, "showsHorizontalScrollIndicator": true, "showsVerticalScrollIndicator": true, "snapToAlignment": true, "snapToEnd": true, "snapToInterval": true, "snapToOffsets": true, "snapToStart": true, "start": true, "style": [Object], "testID": true, "top": true, "transform": [Object], "transformOrigin": true, "width": true, "zIndex": true, "zoomScale": true}}}
LOG {"layout": {"height": 749, "width": 393, "x": 0, "y": 0}, "target": 517}
So it seems like target from event and target from event.nativeEvent have different types. So website has a right definition 😊
Feel free to correct me 🙌
Oh, I see how I have been misreading this.
I dug through the history of the website page, and it looks like https://github.com/facebook/react-native-website/pull/2209 copied one of multiple conflicting existing documented types on this (didn’t look back further).
I also looked through the history of CoreEventTypes Flow types, which is the source of truth. It has never exposed this as part of the native event.
So, I’m pretty sure the website is the wrong thing here (and… also mentions PressEvent instead of LayoutEvent 😅). If we’ve never exposed this member in our source of truth typings, I suspect we should probably keep this way.
If we’ve never exposed this member in our source of truth typings, I suspect we should probably keep this way.
@NickGerleman but this property was added at some point of time and I guess was added with a purpose? Do you want to remove this property in the future from onLayout event? And because of that you don't want to add this property to public types?
While I haven't dug into the implementation details of why we have a member here, if we have never exposed this on Flow types (or TS types for that matter), the source of truth is that it doesn't exist.
I think we would need to deep dive into the implementation to understand the intentionality here, but it seems like the website is the one at odds with the type system, and even the website previously didn't mention the property in places.
@kirillzyusko did you stumble into this because it was something you were using, or just because of the discrepancy.
did you stumble into this because it was something you were using, or just because of the discrepancy.
Yeah, I started to use it here:
https://github.com/kirillzyusko/react-native-keyboard-controller/blob/main/src/components/KeyboardAwareScrollView/index.tsx#L125
Basically I need to have a viewTag and I discovered on a website that I can retrieve it from onLayout event. I know that I also can find a viewTag using findNodeHandle, but at the same time I knew that in strict mode it's throwing a warning about deprecation. And when I saw this warning I thought that findNodeHandle is deprecated in general, but after spending some time and digging into this I found that it looks like it will throw a warning if not-ref param will be passed. So it's still safe to use findNodeHandle if I pass a ref, right?
Will be ahppy if you can shed some light on it, because I haven't found any information on a website and all my guesses are based on the analysis of several popular open source libraries and RN code.
I'm closing this PR as I think it's not going to be merged