Home > front end >  Could someone help me understand how this component class works
Could someone help me understand how this component class works

Time:06-07

import React, { Component, ComponentClass, ComponentType } from "react";

interface State {
  hasError: boolean;
}

const withErrorBoundary = <T extends Record<string, never>>(
  WrappedComponent: ComponentType<T>
): ComponentClass<T, State> =>
  class ErrorBoundary extends Component<T, State> {
    state: { hasError: boolean };

    constructor(props: T) {
      super(props);
      this.state = { hasError: false };
    }

    static getDerivedStateFromError(error: Error) {
      console.error(error);
      return { hasError: true };
    }

    render() {
      if (this.state.hasError) {
        return <p>Something went wrong</p>;
      }
      return <WrappedComponent {...this.props} />;
    }
  };

export default withErrorBoundary;

So I know what this class component does. What I'm trying to understand is <T extends Record<string, never>>(WrappedComponent: ComponentType<T>). I get it's a wrapper class but I can't wrap my head around it. Why is <T extends> defined before the class component? Any resources that can solidify my understanding would be appricted.

CodePudding user response:

T is a generic type parameter. Think of it like a variable for a type.

And it represents the props of the wrapped component so that the wrapper component can have the exact same props and pass them through.

So with this code:

const withErrorBoundary = <T extends Record<string, never>>(
  WrappedComponent: ComponentType<T>
): ComponentClass<T, State> =>
  class ErrorBoundary extends Component<T, State> {}

Let's go line by line.


const withErrorBoundary = <T extends Record<string, never>>(

This is a function named withErrorBoundary which declares the generic parameter T. T is constrained by Record<string, never>. This means that T must be a subtype of the constraint. In other words, whatever T is must be assignable to the constraint.


  WrappedComponent: ComponentType<T>

ComponentType is any react component, and it accepts a generic type for its' props. For example ComponentType<{ a: number }> would be used like <MyComponent a={123} />

So this function accepts a single argument of type ComponentType<T>. This is where T infers its type. Whatever the type of the props of the component passed as WrappedComponent is assigned to the type T, because T appears in the spot where ComponentType expect the props type to be declared.


): ComponentClass<T, State> =>

The function returns a class based react component with props T (the props that WrappedComponent would accept) and a state type of State (which is an interface you've declared elsewhere)


  class ErrorBoundary extends Component<T, State> {}

This line creates a class component that extends a react component that has props of type T and state of type State. This matches the return value of the function, and will be returned by the function.


Lastly, this type:

T extends Record<string, never>

is a little strange. A Record typed like that means an empty object (like {}). So this function may only take components that have no props. Which doesn't make much sense:

const MyComp = () => <>test</>
const TestComp = withErrorBoundary(MyComp) // fine

const MyCompA = ({a}: { a: number }) => <>{a}</>
const TestCompA = withErrorBoundary(MyCompA) // type error

See playground

Instead, I'm guessing this type was intended:

T extends Record<string, unknown>

This means that T is constrained by the type of an object with string keys and unknown typed values. This means the wrapped component can have any props.

See playground

  • Related