Home > Blockchain >  Fixing types for mapping over object to create React components
Fixing types for mapping over object to create React components

Time:08-25

I really like uing this pattern for rendering similar components and using a _type prop to distinguish it and pass it down to the correct component.

However, I've found it difficult to add in the types correctly and was wondering if you guys could help. I have some questions;

  1. Is the BlockMap type correct?
  2. What type should I be using for ResolvedBlock?
  3. Or generally, is there a better way of writing the types (without changing this structure?)
    import React from 'react'
    import { ImageBlock } from '/ImageBlock' // Assume all components are imported from whereever
    
    type BlockType =
    | 'imageBlock'
    | 'formBlock'
    | 'statisticBlock'
    | 'videoBlock'
    | 'quoteBlock'
    
    interface Block {
      _type: BlockType
      _key: string
      heading?: string
      backgroundColor?: string
      theme?: 'dark' | 'light'
    }
    
    type BlockMap = Record<BlockType, JSX.Element> // Is this type correct?
    
     const blockMap:BlockMap = {
      imageBlock: ImageBlock,
      formBlock: FormBlock,
      statisticBlock: StatisticBlock,
      videoBlock: VideoBlock,
      quoteBlock: QuoteBlock,
    }
    
    interface Props {
      className?: string
      blocks: Block[]
    }
    
    export function BlocksBuilder({
      blocks = [],
      className = ``,
    }: Props):JSX.Element {
      return (
        <>
          {blocks.map(block => {
            const ResolvedBlock = blockMap[block._type] // What type should ResolvedBlock be?
            if (!ResolvedBlock) return null
    
            return (
                <ResolvedBlock
                  className={className}
                  block={block}
                  key={block._key}
                />
            )
          })}
        </>
      )
    }

CodePudding user response:

It's a good pattern, and your type is close, but you will want to define the shape of your components instead.

type BlockMap = Record<BlockType, (props: any) => JSX.Element>

You could also define the props for Block components, and use that type for each of your components

interface BlockProps {
  key: string;
  block: Block;
  classname: string;
}

type BlockComponent = (props: BlockProps) => JSX.Element;

export const ImageBlock: BlockComponent = (props) => {
  return <></>;
};
type BlockMap = Record<BlockType, BlockComponent>
  • Related