Higher Order Components

Higher Order Functions

A higher-order function is a function that does at least one of the following:

  • takes one or more functions as arguments
  • returns a function as its result

Wikipedia

What?

A higher-order component is a function that takes a component and returns a new component.

React docs

  • const ComponentWithFeature = withFeature(InternalComponent)
  • usually for injecting props in the wrapped component

Why?

withTheme

import React, { Component, ComponentType, ReactNode } from 'react'
import { ThemeContext, ThemeContextType } from './internal/theme-context'

export interface PropsWithTheme {
  theme: ThemeContextType
}
export type PropsWithoutTheme<T> = Omit<T, 'theme'>

export const withTheme = <TProps extends PropsWithTheme>(
  WrappedComponent: ComponentType<TProps>,
): ComponentType<PropsWithoutTheme<TProps>> => {
  type WithThemeComponentProps = PropsWithoutTheme<TProps>

  return class WithThemeComponent extends Component<WithThemeComponentProps> {
    static displayName = `withTheme(${
      WrappedComponent.displayName || WrappedComponent.name || 'Component'
    })`

    private themeContextConsumer = (theme: ThemeContextType): ReactNode => {
      const props = { ...this.props, theme } as TProps
      return <WrappedComponent {...props} />
    }

    render = (): ReactNode => {
      return (
        <ThemeContext.Consumer>
          {this.themeContextConsumer}
        </ThemeContext.Consumer>
      )
    }
  }
}
import React, { Component, FC, ReactNode } from 'react'
import {
  DarkTheme,
  LightTheme,
  PropsWithTheme,
  withTheme,
} from './theme-library'

interface InternalThemedParagraphProps extends PropsWithTheme {
  children: ReactNode
}
class InternalThemedParagraph extends Component<InternalThemedParagraphProps> {
  componentDidMount = () => {
    // eslint-disable-next-line no-console
    console.log(this.props.theme)
  }

  render = () => {
    const {
      theme: {
        color: { foreground },
      },
      children,
    } = this.props
    return <p style={{ color: foreground }}>{children}</p>
  }
}
export const ThemedParagraph = withTheme(InternalThemedParagraph)

interface InternalThemedCardProps extends PropsWithTheme {
  children: ReactNode
}
const InternalThemedCard: FC<InternalThemedCardProps> = ({
  theme: {
    color: { background },
  },
  children,
}) => {
  return <div style={{ backgroundColor: background }}>{children}</div>
}
export const ThemedCard = withTheme(InternalThemedCard)

export default (
  <>
    <DarkTheme>
      <ThemedCard>
        <ThemedParagraph>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta ipsum
          sapiente ut pariatur. Aliquam suscipit consequuntur similique repellat
          praesentium eos odit. Ad ipsum voluptatibus natus dignissimos rerum.
          Laborum, molestias temporibus?
        </ThemedParagraph>
        <ThemedParagraph>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta ipsum
          sapiente ut pariatur. Aliquam suscipit consequuntur similique repellat
          praesentium eos odit. Ad ipsum voluptatibus natus dignissimos rerum.
          Laborum, molestias temporibus?
        </ThemedParagraph>
      </ThemedCard>
    </DarkTheme>
    <LightTheme>
      <ThemedCard>
        <ThemedParagraph>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta ipsum
          sapiente ut pariatur. Aliquam suscipit consequuntur similique repellat
          praesentium eos odit. Ad ipsum voluptatibus natus dignissimos rerum.
          Laborum, molestias temporibus?
        </ThemedParagraph>
        <ThemedParagraph>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta ipsum
          sapiente ut pariatur. Aliquam suscipit consequuntur similique repellat
          praesentium eos odit. Ad ipsum voluptatibus natus dignissimos rerum.
          Laborum, molestias temporibus?
        </ThemedParagraph>
      </ThemedCard>
    </LightTheme>
  </>
)

Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta ipsum sapiente ut pariatur. Aliquam suscipit consequuntur similique repellat praesentium eos odit. Ad ipsum voluptatibus natus dignissimos rerum. Laborum, molestias temporibus?

Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta ipsum sapiente ut pariatur. Aliquam suscipit consequuntur similique repellat praesentium eos odit. Ad ipsum voluptatibus natus dignissimos rerum. Laborum, molestias temporibus?

Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta ipsum sapiente ut pariatur. Aliquam suscipit consequuntur similique repellat praesentium eos odit. Ad ipsum voluptatibus natus dignissimos rerum. Laborum, molestias temporibus?

Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta ipsum sapiente ut pariatur. Aliquam suscipit consequuntur similique repellat praesentium eos odit. Ad ipsum voluptatibus natus dignissimos rerum. Laborum, molestias temporibus?