Home > database >  Typescript React Native - "Property 'x' does not exist on type 'IntrinsicAttri
Typescript React Native - "Property 'x' does not exist on type 'IntrinsicAttri

Time:09-14

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>
  );
  • Related