question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Content wrapped inside of ContextMenu gets invisible after navigating away

See original GitHub issue

I’ve first created an issue on react-navigation, but found out that this issue is related to this library.

Current behavior

I have a FlatList (with windowSize: 1) of messages in my chat page, but when I navigate to a new page the items in this list turn blank and have to be rerendered to be visible again.

https://user-images.githubusercontent.com/34555296/151677107-527ea44e-7b3e-41ea-a9b2-ee37a0cdf2e9.mp4

Also interesting to note, when I navigate back, the messages stay visible, however when I navigate to a new page they turn blank in the process of navigating.

Navigate back to overview page: Navigate to call page:

Expected behavior

The items shouldn’t turn blank and have to be rerendered to be visible again.

Environment

package version
react-native-ios-context-menu 1.7.2
@react-navigation/native 6.0.6
@react-navigation/native-stack 6.2.5
react-native-screens 3.10.1
react-native 0.66.4
node 16.10.0
npm or yarn 1.22.15
OS iOS 15.2.1

Reproduction

Snack doesn’t work, because expo doesn’t have react-native-ios-context-menu as a native dependency, but you can use the code to reproduce it:

import * as React from 'react';
import { Text, View, Button, ScrollView } from 'react-native';
import { ContextMenu } from 'react-native-ios-context-menu';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import {
  NavigationContainer,
  useNavigationContainerRef,
} from '@react-navigation/native';

const Stack = createNativeStackNavigator();

function Chat({ navigation }) {
  return (
    <View style={{ flex: 1 }}>
      <Button title="Call" onPress={() => navigation.navigate('call')} />
      <ScrollView style={{ flexGrow: 1 }}>
        {['hi', 'test', 'nice', 'no', 'ok', 'goodbye', 'hello'].map((x) => (
          <ContextMenu
            previewConfig={{ previewType: 'DEFAULT', borderRadius: 0 }}
            menuConfig={{
              menuTitle: '',
              menuItems: [
                {
                  menuTitle: 'React',
                  menuItems: ['👍', '👎', '😂', '❤️', '🚀'].map((x) => ({
                    actionKey: 'react-' + x,
                    actionTitle: x,
                  })),
                  icon: {
                    type: 'IMAGE_SYSTEM',
                    imageValue: {
                      systemName: 'smiley',
                    },
                  },
                },
                {
                  menuTitle: 'Delete',
                  // actionKey: "delete",
                  // actionTitle: "Delete",
                  menuOptions: ['destructive'],
                  menuItems: [
                    {
                      actionKey: 'delete',
                      actionTitle: 'Confirm',
                      menuOptions: ['destructive'],
                      menuAttributes: ['destructive'],
                      icon: {
                        type: 'IMAGE_SYSTEM',
                        imageValue: {
                          systemName: 'trash.fill',
                        },
                      },
                    },
                  ],
                  icon: {
                    type: 'IMAGE_SYSTEM',
                    imageValue: {
                      systemName: 'trash.fill',
                    },
                  },
                },
              ],
            }}>
            <View style={{ padding: 10, margin: 5 }}>
              <Text>{x}</Text>
            </View>
          </ContextMenu>
        ))}
      </ScrollView>
    </View>
  );
}

function Call() {
  return (
    <View style={{ flex: 1 }}>
      <Text style={{ fontSize: 30 }}>Call Page</Text>
    </View>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="chat" component={Chat} />
        <Stack.Screen name="call" component={Call} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:13 (4 by maintainers)

github_iconTop GitHub Comments

4reactions
dominicstopcommented, Sep 24, 2022

@fobos531 hello, sorry for the late reply, haven’t been active w/ OSS lately afdghsjdjaklfjklsghshgjdfogjdf

Basically, to prevent memory leaks, a cleanup routine needs to be triggered whenever the component unmounts.

The way I’ve implemented this is through a parent view controller that listens for when a view is removed (e.g. RNINavigationEventsReportingViewController).

The UIVIewController.viewWillDisappear lifecycle method gets called whenever a view is hidden. Inside that method, we check if the parent UINavigationController’s navigation stack still contains the parent view controller.

If it does, then that means the current view controller hasn’t been poppped yet, and instead, a new view controller has been pushed into the stack (and has taken focus).

Otherwise it means thats the view controller has been popped, and thus, it should notify its delegate (i.e. the RNIContextMenu instance) that it’s going to be removed from the navigation stack.

This in turn will trigger the clean up routine.



There are two assumptions here: The 1st one is that the parent view controller is the root view controller for the screen (which might not always be the case).

The 2nd assumption is that the reason for UIVIewController.viewWillDisappear to trigger is due to a UINavigationController instance changing the current active vc.

Unfortunately, if either one of these assumptions are false, then this will result w/ the cleanup routine being called when it doesn’t need to be — causing the “disappearing” bug.



In the latest release (version 1.12.2), I added a internalCleanupMode prop. This prop controls how the cleanup routine is triggered.

You can set this prop to use 4 modes: automatic, viewController, reactComponentWillUnmount, and disabled — normally, you shouldn’t need to fiddle with this prop, but I’ve added it as temp. fix for when this bug happens again (and I can’t immediately fix it).

viewController mode will trigger the clean up routine via the UIViewController.viewWillDisappear lifecycle method, while reactComponentWillUnmount mode on the other hand, will trigger the clean up routine via the componentWillUnmount react lifecycle event.

By default, the internalCleanupMode prop is set to automatic. And currently, automatic mode defaults to using reactComponentWillUnmount mode — I might change this back to viewController once i fix all the edge cases.



If you are still experiencing this issue, try setting internalCleanupMode to disabled and reopen this issue (feel free to tag me if I take too long respond).

But for now, I’ll be closing this issue asjhskflehfkehfiljds;jflsi @Flam3rboy sorry for late response, but i hope this fixes your issue xx

3reactions
ferrannpcommented, May 24, 2022

Hey @dominicstop. I patched the library as follows and it has been working for me so far:

  // Instead of viewWillDisappear
  override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated);
    
    guard let parentVC = self.parentVC
    else { return };
    
    let navVC = self.navigationController;
    // if parent VC still exist in the stack, then it hasn't been popped yet
    let isPopping = navVC == nil || !(navVC?.viewControllers.contains(parentVC) ?? true);
    ...

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to prevent context menu in an iframe? - Stack Overflow
The only way to prevent the context menu inside an iframe is to load your context menu prevention script inside the iframe itself....
Read more >
How to: Use a Custom Context Menu with a TextBox - WPF ...
Implement a Custom Context Menu​​ To restore the default context menu, use the ClearValue method to clear the value of the ContextMenu property. ......
Read more >
Custom Right Click with Context Menu in React - YouTube
Code: https://github.com/stuyy/ context-menu -reactSupport the Channel:Become a Member: https://www.youtube.com/ansonthedeveloper/joinBecome a ...
Read more >
<nav>: The Navigation Section element - MDN Web Docs
The HTML element represents a section of a page whose purpose is to provide navigation links, either within the current document or to...
Read more >
ContextMenu | Apple Developer Documentation
You can create a context menu by first defining a ContextMenu container with the controls that represent the actions people can take, and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found