Home > Software design >  How convert class component to functional with function constructor?
How convert class component to functional with function constructor?

Time:11-08

I am trying to convert this class component to a function component but I need the component to have these static properties.

export class InputBox<T extends string|number|undefined = undefined> extends React.Component<IInputBoxProps<T>, IInputBoxState> {
    static displayName = "InputBox";
    static defaultProps: IInputBoxProps<string|number> = {
        disabled: false,
        rightAligned: false,
        id: "",
        keySelectIndex: 0,
    }

... rest of component
}

I have tried this code:

export const InputBox: React.FunctionComponent<IInputBoxProps<any>> = <T extends string | number | undefined = undefined >(props: IInputBoxProps<T>) => {
    InputBox.displayName = "InputBox";
    InputBox.defaultProps = {
        disabled: false,
        rightAligned: false,
        id: "",
        keySelectIndex: 0,
    }

... rest of component
}

When I run this code I get a eslint error saying that:

FunctionComponent<IInputBox<any>>' is not a constructor function type.

T = any does not work here either. I know any kinda makes typescript useless but I can't seem to get around the error.

How can I type a function component with a "constructor function type" and what does that mean exactly?

What is the difference between a function constructor and class constructor in a class constructor? Should I type static props differently in a function component?

CodePudding user response:

Generic Component

export const InputBox: React.FunctionComponent<IInputBoxProps<any>> = <T ...

This sort of syntax will never work properly because you are trying to set the generic T on the function itself. The point of the generic is that the T can be different every time the function is called.

So you cannot use the React.FunctionComponent type here. But you don't need to -- just set the types of the props.

If your eslint settings uses the rule explicit-module-boundary-names then you will also need to set the return type as JSX.Element (or JSX.Element | null if it might return null).


Default Props

defaultProps doesn't really exist on function components (See discussion here). Rather than trying to set the defaultProps property on your function component, think about what that property is actually doing and how you can implement that functionality.

I would handle this by using the values from your defaultProps as the defaults when destucturing the properties. (Note: I'm not sure what the T here applies to).

const InputBox = <T extends string | number | undefined = undefined>(
    props: IInputBoxProps<T>
) => {
    const {
        disabled = false,
        rightAligned = false,
        id = "",
        keySelectIndex = 0,
        // ... other props without defaults
    } = props;
...

or inline

const InputBox = <T extends string | number | undefined = undefined>(
    { disabled = false, rightAligned = false, id = "", keySelectIndex = 0, data }: IInputBoxProps<T>
) => {
...

Display Name

The displayName is not really needed because the inferred display name will be the name of the function. It will already be "InputBox". You should only be getting eslint errors here if you have used the option { ignoreTranspilerName: true }.

As you can see in the rule examples, you must first create the function and then assign the displayName property to the function. Aleksey's comment does this correctly.

const InputBox = <T extends string | number | undefined = undefined>(
    props: IInputBoxProps<T>
): JSX.Element => {
    /* ... */
}
InputBox.displayName = "InputBox";
  • Related