React-native-screens: headerRight randomly position the component incorrectly

Created on 21 Mar 2020  路  44Comments  路  Source: software-mansion/react-native-screens

I was testing the createNativeStackNavigation and ended up having this random issue where the component rendered by headerRight is being positioned incorrectly sometimes. There's nothing special in my code, I am just rendering a <Button> from react-native.

Here's the source code: https://github.com/lnmunhoz/react-native-experiments/blob/master/react-navigation-examples/examples/NativeNavigation.tsx.

Kapture 2020-03-21 at 20 56 50

Update

The issue also happens when the headerLargeTitle is false.

image

bug native-stack

Most helpful comment

I have a workaround if it's a blocker for anyone here https://github.com/react-navigation/react-navigation/issues/6746#issuecomment-583897436:

As a workaround I show the Icon after a few ms:

const [shouldRenderIcon, setShouldRenderIcon] = useState(false)

useEffect(() => {
const nbr = setTimeout(() => setShouldRenderIcon(true), 200)
return () => clearTimeout(nbr)
}, [])

return shouldRenderIcon ? <MyIcon /> : null

All 44 comments

I cannot repro your issue either on a simulator or physical device. Upgrade packages to the newest version. Is the issue still present then? If so, please try to write which steps to take for the issue to occur.

Yes, I just tried again and the issue is still present. I did a fresh install and the issue persisted. Have you tried the repo I've sent?

The steps are simple:

  • Clone the repo
  • cd repo/react-navigation-examples
  • yarn && yarn start
  • And then open in iOS simulator

To create the stack I am using createNativeStackNavigator from react-native-screens/native-stack.

I created a snack to reproduce but also not being able to make it happen there:

The thing is, I am using a stack created by createNativeStackNavigator inside a stack from createStackNavigator.

I wanted to have parts of my app using the native stack for more simple screens where I don't need to manipulate the header too much, and others the js based stack so I can migrate incrementally.

Is that an issue or should work fine?

I cloned your project and also made a bare react-native project. The issue exists only while opening it through Expo Client. This means that the version of RNScreens in the current Expo Client is having the bug since it has RNScreens in version 2.0.0-alpha.12. Can you repro it that way to prove if it is right?

@lnmunhoz can I close it then? Is your issue solved?

Let me try to reproduce one more time so we can figure out why it was happening.

I will update here in a couple hours.

This is happens in bare RN project as well, so I doubt it has anything to do with Expo

@Deepp0925 does it happen with the newest RNScreens version: 2.4.0?

Yes sir

I have a workaround if it's a blocker for anyone here https://github.com/react-navigation/react-navigation/issues/6746#issuecomment-583897436:

As a workaround I show the Icon after a few ms:

const [shouldRenderIcon, setShouldRenderIcon] = useState(false)

useEffect(() => {
const nbr = setTimeout(() => setShouldRenderIcon(true), 200)
return () => clearTimeout(nbr)
}, [])

return shouldRenderIcon ? <MyIcon /> : null

I can also confirm that this bug still happens on [email protected] and [email protected].

Hey @WoLewicki, do you want me to setup a sample project, showing this issue? Here's some screenshots demonstrating the problem:

This first one, is just adding a 44x44 Touchable button as the headerRight - you can see it's quite far to the left.
Screenshot 2020-04-30 at 17 45 37

If I inspect the element, you can see it's 44x44:
Screenshot 2020-04-30 at 17 51 30

If I add a marginRight: -20, it does "fix" the alignment, but not the issue itself.
Screenshot 2020-04-30 at 17 46 22

Now if I inspect the element, you can see it's only 24px wide, so almost half of the button is cropped and not easy to tap.
Screenshot 2020-04-30 at 17 46 44

It feels like this extra 20px on the right side added by the native wrapper is causing the problem - if that space would be removed entirely the problem would be solved. The back button works this way if you inspect it:

Screenshot 2020-04-30 at 17 54 17

Using a custom compontent in headerCenter is affected by the same bug/problem...

@osdnk

same for me

This happens almost everytime I start the app, this really should be fixed.

I believe it is the same issue as #322. Can you try the workaround submitted there and see if it works correctly?

same for me

same for me too

This is happening to me also with headerCenter and headerRight only in iOS, my custom component is wrongly positioned.
It only displays correctly when the default back button is present.

Same problem Im having

const Stack = createNativeStackNavigator();
const AccountsScreen = () => {
return headerRight: () => ,
})}>

...

interface Props {
navigation?: NavigationContainerRef;
}
export const HeaderRight = (props: Props) => {
const dispatch = useDispatch();
const {navigation} = props;
return

}

Sometimes the Text gets pushed off screen in ios, RN .62 with latest React Navigation.
Screen Shot 2020-06-25 at 4 44 38 PM

Screen Shot 2020-06-25 at 4 43 40 PM

Same here using Expo SDK v38, which relies on ~2.9.0.

+1

Closed https://github.com/software-mansion/react-native-screens/issues/571 for this as seems to be related. I shouldn't be the one to dictate priority but this seems really important, it's really not ideal to have production apps experience this.

Also I have a feeling mentioning expo is not relevant, I experience this consistently without.

Can you check if #600 resolves the issue and if it does not introduce other unwanted behaviors?

600 should be ready now. Can you check if it does not destroy any of your behavior? @kyle-ssg @lnmunhoz

@WoLewicki I've been testing #600 in my app (where I could occasionally reproduce the issue) and haven't seen it since I installed your PR. Haven't seen any sort of regression either.

So, from my perspective, it looks good!

@FrankFundel can you check if #613 resolves your problem introduced in #600? Also, @spencercarli can you check if it does not destroy the good behavior of #600? @kyle-ssg @lnmunhoz @matt-oakes can you check if #600 + #613 resolve the bugs you discovered?

This has been on my todo for a few days, I'll prioritise testing this in the evening @WoLewicki, thanks for letting me know again.

@WoLewicki this does not fix my problem :(

@FrankFundel can you provide a repo/snack that shows the problem then?

@WoLewicki This 100% fixes my issue!

My replication is very straight forward, just do something along the lines of this:

 <Stack.Screen ...  
   initialParams={{
     screenOptions: {
         headerCenter: ()=>    <ReactNative.Text>Loading</ReactNative.Text>,
     }
  }}
/>

Then in the screen:

    useLayoutEffect(() => {
      if (route?.params?.screenOptions) {
        navigation.setOptions(route.params.screenOptions);
      }
    }, [navigation, route]);

Before
image

After
image

@WoLewicki When I apply #600 the header elements render perfectly 馃憣, but screens in nested navigators (e.g. modals) won't render until after the transition. #613 appears to address the latter issue, but #613 regresses the header element rendering 馃槥

@nickyleach What does #613 it regress exactly? The elements don't align the way they should all the time?

@WoLewicki Yeah, in my testing the combination of #600 and #613 seems like the same behavior as what's currently in 2.10.1

Can you provide a repo in which applying these changes show the wrong aligning again?

613 should have fixed most of the issues. Please comment here if I am wrong. Also, #528 is not fixed for sure, so I will reopen it to keep the discussion about that issue there.

@WoLewicki It is still happening with 2.11.0

@happyfloat can you provide a repo/snack with minimal configuration needed to reproduce the issue?

@WoLewicki It's on my todo list. Hopefully by Monday next week. Including the other modal bug, if I can reproduce everything.

Hey @WoLewicki,

Here's a MVCE (minimally viable complete example) of the issue:
Repo
Issue with GIF

I believe this represents the base issue where a custom header button disappears on rotation. I've had some success following some of the other fixes around, like using a time out, or listening to window dimensions changes to try to force a re-render, but those are all hacks on top of this base problem here, and none of them solve the problem entirely.

Attaching gif here below too:
Screen Recording 2020-10-13 at 9 31 46 AM

I'm running it on a iPad, but it seems to happen on an iPhone as well.

Let me know if you need anything else.

@zibs the issue with items disappearing on rotation is discussed in #528. This issue is about positioning the header items on load, which I believe was fixed. You can check the workaround proposed in there (#644), but I am aware it is not a perfect one.

Hey @WoLewicki thanks for the follow up. Can you check out this updated gif and see if you have any insights? I've integrated the workaround you proposed in #644 into my repo, and it seems to partly fix one issue, but the not issue I'm hunting down in particular:

You can see in this gif that it works fine now while rotating on screen 1, but you if go to screen 2, rotate there, and then come back to screen1 (happens at the end of the gif), the header button is gone again.

Screen Recording 2020-10-14 at 8 38 46 AM

Like I said, the repo is updated if you want to run it, and it's applying your patch when built.

@zibs can you post this comment in #528 to keep the discussion there? Also, you can try to move all the code inside this added method to a new method, and then call it in RNSScreen viewWillAppear, maybe it will fix the issue for when coming back from another screen. Please comment if you need more help with applying this.

Having the same issue on iPhone.

I only see it happening on release mode and it doesn't happen always or in all the screens.

Worked it around by setting headerRight as an empty Text on the screen definition.

const ScreenComponent = ({ navigation }) => {
  useEffect(() => {
    navigation.setOptions({
      headerRight: () => (
        <TouchableOpacity>
          <Text>Button</Text>
        </TouchableOpacity> 
      )
  }, []);

  return <View>...</View>;
}

const App = () => (
  <Stack.Navigator>
    <Stack.Screen
      name="ScreenName"
      component={ScreenComponent}
       options={{
         headerRight: () => <Text> </Text>,
       }}
     />
  </Stack.Navigator>
);

package.json

{
  "name": "App",
  "version": "2.0.0",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint .",
    "postinstall": "patch-package && npx jetify",
    "relay": "relay-compiler --src ./src --schema ./schema.graphql",
    "update_schema": "node scripts/update_schema.js"
  },
  "dependencies": {
    "@expo/react-native-action-sheet": "^3.8.0",
    "@invertase/react-native-apple-authentication": "^1.1.2",
    "@react-native-community/async-storage": "^1.11.0",
    "@react-native-community/blur": "^3.6.0",
    "@react-native-community/cookies": "^4.0.0",
    "@react-native-community/hooks": "^2.6.0",
    "@react-native-community/masked-view": "^0.1.10",
    "@react-native-community/netinfo": "^5.9.5",
    "@react-native-firebase/app": "^8.2.0",
    "@react-native-firebase/messaging": "^7.5.0",
    "@react-navigation/bottom-tabs": "^5.9.2",
    "@react-navigation/drawer": "^5.9.3",
    "@react-navigation/native": "^5.7.6",
    "@react-navigation/stack": "^5.9.3",
    "@rnhooks/async-storage": "^0.0.1",
    "@rnhooks/keyboard": "^0.0.3",
    "axios": "^0.19.2",
    "babel-relay-plugin": "^0.11.0",
    "cookie": "^0.4.1",
    "formik": "^2.1.5",
    "jetifier": "^1.6.6",
    "lodash": "^4.17.20",
    "moment": "^2.29.0",
    "patch-package": "^6.2.2",
    "postinstall-postinstall": "^2.1.0",
    "react": "16.13.1",
    "react-native": "0.63.3",
    "react-native-background-fetch": "^3.1.0",
    "react-native-config": "^1.3.3",
    "react-native-device-info": "^5.6.3",
    "react-native-elements": "^2.3.2",
    "react-native-geocoding": "^0.4.0",
    "react-native-geolocation-service": "^5.0.0",
    "react-native-gesture-handler": "^1.8.0",
    "react-native-gifted-chat": "^0.16.3",
    "react-native-google-fit": "^0.12.2",
    "react-native-image-crop-picker": "^0.35.0",
    "react-native-inappbrowser-reborn": "^3.4.0",
    "react-native-intercom": "^17.0.0",
    "react-native-localize": "^1.4.1",
    "react-native-offline": "^5.7.0",
    "react-native-permissions": "^2.1.5",
    "react-native-raw-bottom-sheet": "^2.2.0",
    "react-native-reanimated": "^1.13.1",
    "react-native-safe-area-context": "^3.1.7",
    "react-native-screens": "^2.11.0",
    "react-native-side-menu": "^1.1.3",
    "react-native-vector-icons": "^7.0.0",
    "react-native-version-number": "^0.3.6",
    "react-native-webview": "^10.3.2",
    "react-relay": "^10.0.1",
    "reanimated-bottom-sheet": "^1.0.0-alpha.22",
    "relay-runtime": "^10.0.1",
    "rn-apple-healthkit": "^0.8.0",
    "rollbar-react-native": "^0.9.1",
    "swr": "^0.2.3",
    "url": "^0.11.0",
    "yup": "^0.29.3"
  },
  "devDependencies": {
    "@babel/core": "^7.10.4",
    "@babel/runtime": "^7.10.4",
    "@react-native-community/eslint-config": "^2.0.0",
    "babel-jest": "^26.1.0",
    "babel-plugin-relay": "^10.0.1",
    "eslint": "^7.4.0",
    "graphql": "^15.3.0",
    "jest": "^26.1.0",
    "metro-react-native-babel-preset": "^0.60.0",
    "react-test-renderer": "16.13.1",
    "relay-compiler": "^10.0.1",
    "relay-config": "^10.0.1"
  },
  "jest": {
    "preset": "react-native"
  }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

joshua-augustinus picture joshua-augustinus  路  4Comments

harrisrobin picture harrisrobin  路  3Comments

OmarBasem picture OmarBasem  路  4Comments

chai86 picture chai86  路  3Comments

dotconnor picture dotconnor  路  5Comments