react-native-screens icon indicating copy to clipboard operation
react-native-screens copied to clipboard

While using `measureInWindow`, the `y` value is off

Open mrzachnugent opened this issue 1 year ago • 8 comments

Description

Reposting issue reported here as asked: https://github.com/react-navigation/react-navigation/issues/12294

Given a useLayoutEffect that calls the measureInWindow of a ref , the y value is off. It seems to be off by the header height on iOS and web. For Android, I'm not sure what is off by exactly.

When the header is transparent or not shown, the y value from measureInWindow is correct for iOS and web but it is still off on Android.

// In a screen with default options

  React.useLayoutEffect(() => {
    triggerRef.current?.measureInWindow((x, y, width, height) => {
      setTargetRect({ x, y, width, height });
    });
  }, [setTargetRect]);

 // ...

  <Pressable ref={triggerRef}
  // ...
  
  // This should place it right below the trigger
  <View 
    style={{
    // ...
      position: "absolute",
      top: targetRect.y + targetRect.height, 
    }}
  // ...

Steps to reproduce

  1. Clone the minimal reproduction repo: https://github.com/mrzachnugent/react-navigation-measure-in-window-repro
  2. Run npm install && npm run dev to install the dependencies and start Expo Go in an iOS simulator.
  3. Press the "Press here to open" button, the red square should appear right below the button.
  4. Try with web by pressing w in the terminal running metro
  5. Try with android by pressing a in the terminal running metro
  6. Toggle to a transparent header by running git checkout transparent-header, reload the apps and see the difference.

Snack or a link to a repository

https://github.com/mrzachnugent/react-navigation-measure-in-window-repro

Screens version

4.1.0

React Native version

0.76.3

Platforms

Android, iOS, Web

JavaScript runtime

Hermes

Workflow

Expo managed workflow

Architecture

Fabric (New Architecture)

Build type

None

Device

iOS simulator

Device model

No response

Acknowledgements

Yes

mrzachnugent avatar Nov 24 '24 22:11 mrzachnugent

The same is also true for pageY in .measure() when measuring an element inside a native stack header. (You get a large negative number in iOS)

casperstr avatar Dec 03 '24 15:12 casperstr

Having the same issue. y is always a large negative number. Has anyone found a workaround in the meantime?

haeniya avatar Feb 07 '25 14:02 haeniya

Having the same issue. y is always a large negative number.

Hey, this is intended (and accurate) in case of items in header config, because the header is positioned above the screen, despite being its child in react element tree.

kkafar avatar Apr 15 '25 14:04 kkafar

As for original issue - we're aware of it, however this won't be fixed any time soon. We have to do many hacky workarounds to make pressable work on new architecture and the values you get are result of these hacks.

kkafar avatar Apr 16 '25 07:04 kkafar

Hi @kkafar , thanks for the response.

Does that mean that you suggest to use anything else but a Pressable for this (like a TouchableOpacity for example)?

In the React-Native docs, they suggest using a Pressable for a more "future proof" solution. image

mrzachnugent avatar Apr 16 '25 12:04 mrzachnugent

Does that mean that you suggest to use anything else but a Pressable for this (like a TouchableOpacity for example)?

@mrzachnugent I think I do not understand exactly what do you mean by "[...] for this".

The workarounds I've told you apply to any pressable/touchable component that is based on shadow state (i.e. any such component from react-native). These should however work just fine that's why we have our hacky solutions in place.

In case you experience some bugs related to pressables / touchables that are not existent w/o presence of react-native-screens please let me know - I'll be happy to engage in fixing them.

kkafar avatar Apr 16 '25 12:04 kkafar

@kkafar Sorry, I don't think I understood the comment properly:

As for original issue - we're aware of it, however this won't be fixed any time soon. We have to do many hacky workarounds to make pressable work on new architecture and the values you get are result of these hacks.

I understood that the issue is with Pressable and the new architecture but what you are saying is that it is not just with Pressable, it's with every component based on the shadow tree, correct?

As for the "workarounds", I'm a little confused as I understood that it "won't be fixed any time soon" and I must of missed the suggested workaround.

mrzachnugent avatar Apr 16 '25 14:04 mrzachnugent

Hey, this is intended (and accurate) in case of items in header config, because the header is positioned above the screen, despite being its child in react element tree.

Hey @kkafar thanks for the reply.

So I have custom dropdown in the headerRight section and I'm doing the following to position the dropdown below the trigger icon:

const toggleDropdown = () => {
    if (isVisible) return;

    triggerRef.current?.measureInWindow((x, y, width, height) => {
      const screenWidth = Dimensions.get('window').width;

      const rightPosition = screenWidth - (x + width); // Align dropdown's right edge with the trigger's right edge

      setDropdownPosition({
        y: y + height,
        right: rightPosition,
      });

      setIsVisible(true);
    });
  };

This works perfectly fine with [email protected] & @react-navigation/[email protected] but it doesn't work anymore with [email protected] & @react-navigation/[email protected] using the new architecture.

If this large negative number for y now is intended, could you please explain how it is calculated and how I now need to calculate the y value for my dropdown?

Many thanks.

haeniya avatar Apr 17 '25 19:04 haeniya