(Typescript React) Inject Props With HOCs

August 18, 2021

Code Example:

This is the code I found most clean that works.

https://maxrozen.com/implement-higher-order-component-react-typescript

import React, { useState } from "react"
// First we need to add a type to let us extend the incoming component and.
type ExtraInfoType = {
  extraInfo: string
}
// Mark the function as a generic using P (or whatever variable you want)
export function withExtraInfo<P>(
  // Then we need to type the incoming component.
  // This creates a union type of whatever the component
  // already accepts AND our extraInfo prop
  WrappedComponent: React.ComponentType<P & ExtraInfoType>
): React.ComponentType<P> {
  const [extraInfo, setExtraInfo] = useState("")
  setExtraInfo("important data.")
  const ComponentWithExtraInfo = (props: P) => {
    // At this point, the props being passed in are the original props the component expects.
    return <WrappedComponent {...props} extraInfo={extraInfo} />
  }
  return ComponentWithExtraInfo
}

In ExtraInfoType, we define the props we are going to inject into the component wrapped by the HOC.

I added the return type of the HOC React.ComponentType<P> to make typing more specific.

P indicates the type of the props of the wrapped component(not including the injected props), and therefore, ExtraInfoType & P will be the type of the props of the wrapped component(including the injected props).

Having injected props with type ExtraInfoType, the returned component's props' type should be P .

Apply more than one HOC

Sometimes, you may want to apply multiple HOCs to the same component.

Since compose cannot correctly infer the returned component, you may need to provide the type for compose `React.ComponentType` using generics like this: compose<React.ComponentType>

import { jsx } from "theme-ui"
import withLogData from "@/containers/withLogData"
import { PageHeader, Layout } from "antd"
import LogTable from "./VoucerLogTable"
import { compose } from "@reduxjs/toolkit"
import withDownloadLog from "@/containers/withDownloadLog"
import React from "react"

const LogPage = (props: Props) => {
  const TableWithData = compose<React.ComponentType>(
    withLogData,
    withDownloadLog
  )(LogTable)

  return (
    <div>
      <PageHeader
        className="site-page-header"
        title="log table"
        backIcon={false}
      />
      <Layout sx={{ minHeight: "100%", padding: "1rem" }}>
        <TableWithData />
      </Layout>
    </div>
  )
}

export default LogPage

Subscribe to my email list

© 2024 ALL RIGHTS RESERVED