I'm trying to create an abstraction for a component by passing a component as a prop. In short, I want to be able to pass a TriggerComponent
that can be either a View
, Button
, or Whatever
other component as long as it can take an onPress
prop.
I have a shared Menu
component here:
// src/components/shared/Menu.tsx
import { ReactNode, FunctionComponent, useRef } from 'react';
import {
Menu as PMMenu,
MenuOptions,
MenuTrigger,
withMenuContext,
} from 'react-native-popup-menu';
import { View } from 'react-native';
interface IMenu {
children: ReactNode,
ctx: {
menuActions: {
toggleMenu: (name: string) => void,
},
},
TriggerComponent: FunctionComponent,
triggerProps: Object,
}
function Menu({
ctx: { menuActions: { toggleMenu } },
children,
TriggerComponent,
triggerProps,
}: IMenu)
...
return (
<View>
<TriggerComponent
{...triggerProps}
onPress={() => toggleMenu(menuRef.current?.props?.menuName)} // <-- ERROR: Property 'onPress' does not exist on type 'IntrinsicAttributes'.ts(2322)
/>
<PMMenu>
<MenuTrigger ref={menuRef} />
<MenuOptions customStyles={optionsStyles}>
{children}
</MenuOptions>
</PMMenu>
</View>
);
The code calling the Menu
component:
function UserHeaderButton() {
const nav = useNavigation<any>();
return (
<Menu
TriggerComponent={Item}
triggerProps={{
title: 'Account',
iconName: 'account-circle',
}}
>
<MenuOption onSelect={() => nav.navigate('auth', { screen: 'login' })} text="Login" />
<MenuOption onSelect={() => nav.navigate('auth', { screen: 'signup' })} text="Sign Up" />
</Menu>
);
}
If I define triggerProps
as any
, the error disappears, which is weird because the onPress
prop isn't included in that object.
CodePudding user response:
You can do the the following.
interface ITriggerProps {
title: string
iconName: string
}
interface IMenu {
children: ReactNode,
ctx: {
menuActions: {
toggleMenu: (name: string) => void,
},
},
TriggerComponent: React.FC<ITriggerProps>
}
Then you can pass a component like the following.
<Menu
TriggerComponent={<Item title="Title" iconName="some-icon" />}
>
This is also completely type-safe. Hope this helps.
CodePudding user response:
Here's what I ended up doing.
In my Menu
component:
// src/components/shared/Menu.tsx
import { ReactNode, FunctionComponent, useRef } from 'react';
import {
Menu as PMMenu,
MenuOptions,
MenuTrigger,
withMenuContext,
} from 'react-native-popup-menu';
import { View } from 'react-native';
interface ITriggerProps {
onPress: Function,
}
interface IMenu {
children: ReactNode,
ctx: {
menuActions: {
toggleMenu: (name: string) => void,
},
},
TriggerComponent: FC<ITriggerProps>,
triggerProps: Object,
}
function Menu({
ctx: { menuActions: { toggleMenu } },
children,
TriggerComponent,
triggerProps,
}: IMenu)
...
return (
<View>
<TriggerComponent
{...triggerProps}
onPress={() => toggleMenu(menuRef.current?.props?.menuName)}
/>
<PMMenu>
<MenuTrigger ref={menuRef} />
<MenuOptions customStyles={optionsStyles}>
{children}
</MenuOptions>
</PMMenu>
</View>
);