Home > Net >  How to programmatically create a component and assign props to the passed component?
How to programmatically create a component and assign props to the passed component?

Time:04-23

i want to create a function that returns the component with the props i want to assign with it? something like this, a reusable component for Text/View/Pressable with extracted styles as props, i personally find it's faster to style with styles as props in React Native:

 const DynamicView = ({
  children,
  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,
  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,
  shadowColor,
  shadowOffset,
  shadowOpacity,
  shadowRadius,
  transform,
  style,
  ...rest
}: ViewStyle & ViewProps) => (
  <View
    style={[
      {
        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,
        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,
        shadowColor,
        shadowOffset,
        shadowOpacity,
        shadowRadius,
        transform,
      } as StyleProp<ViewStyle>,
      style && style,
    ]}
    {...rest}>
    {children}
  </View>
);

this is what i want to do, a reusable function that accepts a Component, e.g. Text, View, Pressable and assign the styles props:

import React from 'react';
import { Pressable, TextStyle, View, ViewProps, ViewStyle } from 'react-native';
type Comp = typeof View | typeof Pressable;
export const createDynamicElement = (RElement: Comp) => {
  // How to dynamically pass TS props?
  // e.g. for Pressable
  class DynamicElement extends React.Component<
    (ViewStyle | TextStyle) & typeof RElement
  > {
    render(): React.ReactNode {
      // How to dynamically extract the styles key?
      // const stylesKeys = keyExtractor(this.props)
      const {
        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,
        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,
        shadowColor,
        shadowOffset,
        shadowOpacity,
        shadowRadius,
        transform,
        ...rest
      } = this.props;
      return (
        <RElement
          {...rest}
          style={[
            {
              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,
              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,
              shadowColor,
              shadowOffset,
              shadowOpacity,
              shadowRadius,
              transform,
            },
          ]}
        />
      );
    }
  }
  return DynamicElement;
};

CodePudding user response:

I am not entirely sure if its a good approach or it would get messy over time.

Something like

getComponent(type:string, style:StyleProps, ...args) {
 switch(type){
   case'div':
    return <div style={style} {...args} />
 case'button':
    return <button style={style} {...args}/>
 case'span':
    return <span style={style}{...args} />
 default:
    return null

   }

}

CodePudding user response:

what i did is declare all ViewStyle keys in an array (extracted from react native typescript definitions):

export const viewStyleKeys = [
  'backfaceVisibility',
  'backgroundColor',
  'borderBottomColor',
  ...
  'shadowRadius',
  'transform',
] as (keyof ViewStyle)[];

create a helper:

    export const handleElementProps = <T>(
      obj: T,
      keys: (keyof ViewStyle | keyof TextStyle)[],
    ) => {
      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 keyof ViewStyle | keyof TextStyle))
      .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,
      });

and use in a reusable view:

const DynamicView = ({ children, style, ...props }: ViewStyle & ViewProps) => {
  const [elementStyles, elementNativeProps] = handleElementProps(
    props,
    viewStyleKeys,
  );
  return (
    <View
      {...elementNativeProps}
      style={[style, elementStyles.element as ViewStyle]}>
      {children}
    </View>
  );
};

it's just preference, think of it like classes but as props, i just don't like to declare styles everywhere

  • Related