enesozturk / react-native-hold-menu

πŸ“± A performant, easy to use hold to open context menu for React Native powered by Reanimated πŸš€
https://enesozturk.github.io/react-native-hold-menu/
MIT License
1.42k stars 97 forks source link

Exposes a `key` value for `MenuItemProps` menu items that are identical other than their `onPress` function are approriately re-rendered #106

Open NoodleOfDeath opened 1 year ago

NoodleOfDeath commented 1 year ago

Hi! πŸ‘‹

Firstly, thanks for your work on this project! πŸ™‚

Today I used patch-package to patch react-native-hold-menu@0.1.6 for the project I'm working on.

The following fixes what is happening in the below screen recording:

https://github.com/enesozturk/react-native-hold-menu/assets/14790443/c129af38-b9ae-428e-b811-0449f014d8e7

Here is the diff that solved my problem (https://github.com/enesozturk/react-native-hold-menu/issues/105):

diff --git a/node_modules/react-native-hold-menu/lib/typescript/components/menu/types.d.ts b/node_modules/react-native-hold-menu/lib/typescript/components/menu/types.d.ts
index 514e5c9..3cddd4b 100644
--- a/node_modules/react-native-hold-menu/lib/typescript/components/menu/types.d.ts
+++ b/node_modules/react-native-hold-menu/lib/typescript/components/menu/types.d.ts
@@ -1,6 +1,7 @@
 import { TransformOriginAnchorPosition } from '../../utils/calculations';

 export type MenuItemProps = {
+  key: string;
   text: string;
   icon?: string | (() => React.ReactElement);
   onPress?: (...args: any[]) => void;
diff --git a/node_modules/react-native-hold-menu/src/components/menu/MenuItems.tsx b/node_modules/react-native-hold-menu/src/components/menu/MenuItems.tsx
index bef7a30..6b48188 100644
--- a/node_modules/react-native-hold-menu/src/components/menu/MenuItems.tsx
+++ b/node_modules/react-native-hold-menu/src/components/menu/MenuItems.tsx
@@ -11,7 +11,7 @@ const MenuItemsComponent = ({ items }: { items: MenuItemProps[] }) => {
       {items.map((item: MenuItemProps, index: number) => {
         return (
           <MenuItem
-            key={index}
+            key={item.key}
             item={item}
             isLast={items.length === index + 1}
           />
diff --git a/node_modules/react-native-hold-menu/src/components/menu/types.d.ts b/node_modules/react-native-hold-menu/src/components/menu/types.d.ts
index 514e5c9..3cddd4b 100644
--- a/node_modules/react-native-hold-menu/src/components/menu/types.d.ts
+++ b/node_modules/react-native-hold-menu/src/components/menu/types.d.ts
@@ -1,6 +1,7 @@
 import { TransformOriginAnchorPosition } from '../../utils/calculations';

 export type MenuItemProps = {
+  key: string;
   text: string;
   icon?: string | (() => React.ReactElement);
   onPress?: (...args: any[]) => void;

Then in your code you will need to specify unique keys that react to whenever you want the item to be rerendered like this:

const items = ['one', 'two', 'three'];
...
<HoldItem items={ items.map((item) => ({
  key: item,
  text: 'share',
  onPress: () => alert(item),
})) }>
  {item}
</HoldItem>

This issue body was partially generated by patch-package.

NoodleOfDeath commented 1 year ago

Would also be nice if the text prop could be a function that returns a string

NoodleOfDeath commented 1 year ago

This is the test code I was using. All of my items have identical menu items except for onPress. If you remove the ability to supply your own unique key, pressing and holding any of the items results in an alert message for whatever button you pressed first, no matter which button you press and hold:

import React from 'react';
import {
  SafeAreaView,
  Text,
  View,
} from 'react-native';

import { HoldItem } from 'react-native-hold-menu';

function Test() {
  const titles = ['one', 'two', 'three'];
  return (
    <View style={ { rowGap: 10 } }>
      {titles.map((title) => (
        <HoldItem
          key={ title }
          items={ [{
            key: title,
            onPress: function test() {
              alert(title);
            },
            text: 'share',
          }] }>
          <Text style={ {
            backgroundColor: '#eee',
            padding: 10,
          } }>
            {title}
          </Text>
        </HoldItem>
      ))}
    </View>
  );
}

export function TestScreen() {
  return (
    <SafeAreaView style={ { flex: 1 } }>
      <View style={ { padding: 24 } }>
        <Test />
      </View>
    </SafeAreaView>
  );
}