Extend an object with a list of entries

  • javascript
posted on 7th December 2021 ∙ by Hynek Svacha ∙ 3 min read

I frequently had to add a list of certain entries (key-value pairs) to an object while I was coding.

Let’s say we have an object mapping Markdown (or MDX) HTML elements to our custom components:

const components = {
  a: props => <CustomLink {...props} />,
  img: props => <Image {...props} />,
  pre: props => {
    const codeBlockProps = preToCodeBlock(props)

    if (codeBlockProps) {
      return <Code {...codeBlockProps} />
    } else {
      return <pre {...props} />
    }
  },
}

What if we want to add every heading tag (to return a custom heading component)? There are six of them (h1 to h6). By all means, we could just add every heading tag manually:

const components = {
  h1: props => <CustomHeading {...props} />,
  h2: props => <CustomHeading {...props} />,
  h3: props => <CustomHeading {...props} />,
  h4: props => <CustomHeading {...props} />,
  h5: props => <CustomHeading {...props} />,
  h6: props => <CustomHeading {...props} />,
  a: props => <CustomLink {...props} />,
  img: props => <Image {...props} />,
  pre: props => {
    const codeBlockProps = preToCodeBlock(props)

    if (codeBlockProps) {
      return <Code {...codeBlockProps} />
    } else {
      return <pre {...props} />
    }
  },
}

I don’t want to evangelize the DRY principle here; I think it can be perfectly fine to repeat ourselves sometimes (see Sandi’s or Dan’s or Kent’s articles).

But let’s say we want to save ourselves the hassle of editing six different lines in our code in the event we want to update the heading component.

We can create an array of all the heading tags and map over them like that:

// Let's assume we want to add an id to every heading tag to allow anchoring:

import slugify from "slugify"

const headings = ["h1", "h2", "h3", "h4", "h5", "h6"].map(headingTag => [
  headingTag,
  ({ children }) => {
    const idText = slugify(children)
    const CustomHeading = headingTag

    return <CustomHeading id={idText}>{children}</CustomHeading>
  },
])

Then, the simplest option is to use Object.fromEntries() method to add the headings into our components object.

The Object.fromEntries method, introduced in the 10th edition of ECMAScript in 2019, converts an iterable into an object. It can be considered an inverse method to the older Object.entries, which does the opposite (turn an object into an iterable).

const components = {
  ...Object.fromEntries(headings),
  a: props => <CustomLink {...props} />,
  img: props => <Image {...props} />,
  pre: props => {
    const codeBlockProps = preToCodeBlock(props)

    if (codeBlockProps) {
      return <Code {...codeBlockProps} />
    } else {
      return <pre {...props} />
    }
  },
}

Now, the component object looks a little tidier.

👍 Enjoy!


If you find anything in this post that should be improved (either factually or in language), feel free to edit it on Github .