'how to properly make a reusable component?

I make reusable component like this:

export type ElementKeysType =
  | keyof ViewStyle
  | keyof TextStyle
  | keyof ImageStyle;

export const handleElementProps = <T>(obj: T, keys: ElementKeysType[]) => {
  const stylesProps = Object.fromEntries(
    keys.filter(key => key in obj).map(key => [key, obj[key as keyof T]]),
  );
  const elementProps = Object.fromEntries(
    Object.keys(obj)
      .filter(key => !keys.includes(key as ElementKeysType))
      .map(key => [key, obj[key as keyof T]]),
  );

  const elementStyles = createStyle(stylesProps);

  return [elementStyles, elementProps];
};

const createStyle = (styles: ViewStyle | ImageStyle | TextStyle) =>
  StyleSheet.create({
    element: styles,
  });


const createWithStylePropsComponent =
  <P extends object>(
    Component: React.ComponentClass<P, any> | React.FunctionComponent<P>,
  ): React.FunctionComponent<P> =>
  ({ style, ...props }: P & { style?: StyleProp<ElementKeysType> }) => {
    // find style keys from props
    const [elementStyles, elementNativeProps] = handleElementProps(props, [
      ...RNViewStyleKeys,
      ...RNTextStyleKeys,
      ...RNImageStylekeys,
    ]);
    return (
      <Component
        {...(elementNativeProps as P)}
        // make component receive style prop
        style={[elementStyles.element, style]}
      />
    );
  };

export const DynamicView = createWithStylePropsComponent<
  ViewProps & ViewProps['style']
>(View);

export const DynamicText = createWithStylePropsComponent<
  TextProps & TextProps['style']
>(Text);

export const DynamicTouchableOpacity = createWithStylePropsComponent<
  TouchableOpacityProps & TouchableOpacityProps['style']
>(TouchableOpacity);

export const DynamicImage = createWithStylePropsComponent<
  ImageProps & ImageProps['style']
>(Image);

export const DynamicPressable = createWithStylePropsComponent<
  PressableProps['style'] & PressableProps
>(Pressable);

when i use it, it's like this:

 <DynamicTouchableOpacity
      flexDirection="row"
      alignItems="center"
      justifyContent="space-between"
      paddingLeft={Pixel(24)}
      paddingVertical={Pixel(24)}
      onPress={onPress}>
      <View>
        <DynamicText
          color={appTheme.typographyHighEmphasis}
          fontSize={Pixel(15)}
          lineHeight={Pixel(24)}
          letterSpacing={Pixel(0.75)}
          fontFamily={fonts.poppinsMedium}>
          {displayDate}
        </DynamicText>
        <DynamicText
          color={appTheme.typographyLowEmphasis}
          fontSize={Pixel(13)}
          lineHeight={Pixel(22)}
          letterSpacing={Pixel(0.25)}
          fontFamily={fonts.poppinsRegular}>
          {remarks}
        </DynamicText>
      </View>
      <DynamicView
        alignItems="center"
        flexDirection="row"
        paddingRight={Pixel(8)}>
        <DynamicView marginRight={Pixel(12)}>
          <DynamicText
            fontSize={Pixel(11)}
            lineHeight={Pixel(16)}
            letterSpacing={Pixel(0.25)}
            fontFamily={fonts.poppinsMedium}
            textAlign="right"
            style={[
              {
                color: isDebitTransaction
                  ? appTheme.typographyPrimary
                  : appTheme.typographyAccent,
              },
            ]}>
            {label}
          </DynamicText>
          <DynamicText
            textAlign="right"
            fontSize={Pixel(13)}
            lineHeight={Pixel(22)}
            letterSpacing={Pixel(0.25)}
            fontFamily={fonts.poppinsSemiBold}
            style={[
              {
                color: isDebitTransaction
                  ? appTheme.typographyPrimary
                  : appTheme.typographyAccent,
              },
            ]}>
            {amount}
          </DynamicText>
          {isDebitTransaction && (
            <DynamicText
              color={appTheme.typographyLowEmphasis}
              fontSize={Pixel(11)}
              lineHeight={Pixel(16)}
              letterSpacing={Pixel(0.25)}
              fontFamily={fonts.poppinsMedium}>
              {`Due ${dueDate}`}
            </DynamicText>
          )}
        </DynamicView>
        <Icon
          name="keyboard-arrow-right"
          size={Pixel(24)}
          color={appTheme.typographyLowEmphasis}
        />
      </DynamicView>
    </DynamicTouchableOpacity>

what i like about it is that with typescript auto suggestion, i save time styling and it's easy to distinguish style props rather than think of another property on StyleSheet as the app gets big and native props for component is auto suggested.

But does doing this slows the app? i mean, every component goes on reusable hook createWithStylePropsComponent. But my idea is like this and maybe want to know suggestions how to improve it?

btw, these are the styles keys extracted from typescript:

import {
  FlexStyle,
  ImageStyle,
  ShadowStyleIOS,
  TextStyle,
  TextStyleAndroid,
  TextStyleIOS,
  ViewStyle,
} from 'react-native';

// keys extracted on react native typescript definitions

const flexStyleKeys = [
  'alignContent',
  'alignItems',
  'alignSelf',
  'aspectRatio',
  'borderEndWidth',
  'borderStartWidth',
  'bottom',
  'display',
  'end',
  'flex',
  'flexBasis',
  'flexDirection',
  'flexGrow',
  'flexShrink',
  'flexWrap',
  'height',
  'justifyContent',
  'left',
  'margin',
  'marginBottom',
  'marginEnd',
  'marginHorizontal',
  'marginLeft',
  'marginRight',
  'marginStart',
  'marginTop',
  'marginVertical',
  'maxHeight',
  'maxWidth',
  'minHeight',
  'minWidth',
  'overflow',
  'padding',
  'paddingBottom',
  'paddingEnd',
  'paddingHorizontal',
  'paddingLeft',
  'paddingRight',
  'paddingStart',
  'paddingTop',
  'paddingVertical',
  'position',
  'right',
  'start',
  'top',
  'width',
  'zIndex',
  'direction',
] as (keyof FlexStyle)[];

const shadowStyleIosKeys = [
  'shadowColor',
  'shadowOffset',
  'shadowOpacity',
  'shadowRadius',
] as (keyof ShadowStyleIOS)[];

const textStyleKeys = [
  'color',
  'fontFamily',
  'fontSize',
  'fontStyle',
  'fontWeight',
  'letterSpacing',
  'lineHeight',
  'textAlign',
  'textDecorationLine',
  'textDecorationStyle',
  'textDecorationColor',
  'textShadowColor',
  'textShadowOffset',
  'textShadowRadius',
  'textTransform',
  'testID',
] as (keyof TextStyle)[];

const textStyleIosKeys = [
  'fontVariant',
  'letterSpacing',
  'textDecorationColor',
  'textDecorationStyle',
  'writingDirection',
] as (keyof TextStyleIOS)[];

const textStyleAndroidkeys = [
  'textAlignVertical',
  'includeFontPadding',
] as (keyof TextStyleAndroid)[];

const viewStyleKeys = [
  'backfaceVisibility',
  'backgroundColor',
  'borderBottomColor',
  'borderBottomEndRadius',
  'borderBottomLeftRadius',
  'borderBottomRightRadius',
  'borderBottomStartRadius',
  'borderBottomWidth',
  'borderColor',
  'borderEndColor',
  'borderLeftColor',
  'borderLeftWidth',
  'borderRadius',
  'borderRightColor',
  'borderRightWidth',
  'borderStartColor',
  'borderStyle',
  'borderTopColor',
  'borderTopEndRadius',
  'borderTopLeftRadius',
  'borderTopRightRadius',
  'borderTopStartRadius',
  'borderTopWidth',
  'borderWidth',
  'opacity',
  'testID',
  'elevation',
] as (keyof ViewStyle)[];

export const RNViewStyleKeys = [
  ...viewStyleKeys,
  ...flexStyleKeys,
  ...shadowStyleIosKeys,
  'transform',
] as (keyof ViewStyle)[];

export const RNTextStyleKeys = [
  ...textStyleKeys,
  ...textStyleIosKeys,
  ...textStyleAndroidkeys,
  ...RNViewStyleKeys,
] as (keyof TextStyle)[];

const imageStyleKeys = [
  'resizeMode',
  'backfaceVisibility',
  'borderBottomLeftRadius',
  'borderBottomRightRadius',
  'backgroundColor',
  'borderColor',
  'borderWidth',
  'borderRadius',
  'borderTopLeftRadius',
  'borderTopRightRadius',
  'overflow',
  'overlayColor',
  'tintColor',
  'opacity',
] as (keyof ImageStyle)[];

export const RNImageStylekeys = [
  ...imageStyleKeys,
  ...flexStyleKeys,
  ...shadowStyleIosKeys,
  'transform',
] as (keyof ImageStyle)[];


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source