Saturday, 1 July 2023

Providing of default props in class components of React TypeScript

Problem

In the below example, it is expected that the value "DEFAULT" will be substituted to optionalPropertyExample when it has not been specified:

type Properties = Readonly<{
  requiredPropertyExample: string;
  optionalPropertyExample: string;
}>;

class Sample extends React.Component<Properties> {

  private static defaultProps: Partial<Properties> = {
    optionalPropertyExample: "DEFAULT"
  };

  public render(): ReactNode {
    // ✓ No TypeScript errors 
    const optionalPropertyExample: string = this.props.optionalPropertyExample;
    // ...
  }

}

But, it we will not specify the optionalPropertyExample when using the Sample as component:

<Sample 
  requiredPropertyExample="Sample"
/>

we'll get the error:

TS2741: Property 'optionalPropertyExample' is missing in type ...'. 

Well, it is because the optionalPropertyExample is required on Properties type. But will it be the solution if to make it optional?

type Properties = Readonly<{
  requiredPropertyExample: string;
  optionalPropertyExample?: string;
}>;

class Sample extends React.Component<Properties> {

  private static defaultProps: Partial<Properties> = {
    optionalPropertyExample: "DEFAULT"
  };


  public render(): ReactNode {
    
    const optionalPropertyExample: string = this.props.optionalPropertyExample;
   // ...

  }

}

Now, we'll get the error

TS2322: Type 'string | undefined' is not assignable to type 'string'.   Type 'undefined' is not assignable to type 'string'.

in the render method. Yes, thanks to defaultProps the this.props.optionalPropertyExample will not be undefined but the problem is how to assure the TypeScript! Without any, variable! and other type safety cracks, off course.

Unacceptable solutions

Just use the Hooks API!

This question is focused on class components. The ones who are fine with hooks API hardly will read this topics.

Substitute the value on decomposing of the object

Easy, but not scalable. Assume that we have N methods method1, method2 ... methodN using optionalPropertyExample where N is the arbitrary large number.

type Properties = Readonly<{
  requiredPropertyExample: string;
  optionalPropertyExample?: string;
}>;

class Sample extends React.Component<Properties> {

  private static defaultProps: Required<Pick<Properties, "optionalPropertyExample">> = {
    optionalPropertyExample: "DEFAULT"
  };


  private method1(): ReactNode {
    const optionalPropertyExample: string = this.props.optionalPropertyExample ?? "DEFAULT";
    // ...
  }

  private method2(): ReactNode {
    const optionalPropertyExample: string = this.props.optionalPropertyExample ?? "DEFAULT";
    // ...
  }

  //

  private methodN(): ReactNode {
    const optionalPropertyExample: string = this.props.optionalPropertyExample ?? "DEFAULT";
    // ...
  }


  public render(): ReactNode {
    
    const optionalPropertyExample: string = this.props.optionalPropertyExample;

  }

}

Now, we want to change the default value of optionalPropertyExample. We need to find all "DEFAULT" occurrences in the code and replace it. There is the risk to forget one of more occurrences or mix up something related.

Maybe we should refer to defaultProps?

type Properties = Readonly<{
  requiredPropertyExample: string;
  optionalPropertyExample?: string;
}>;

class Sample extends React.Component<Properties> {

  private static defaultProps: Required<Pick<Properties, "optionalPropertyExample">> = {
    optionalPropertyExample: "DEFAULT"
  };


  private method1(): ReactNode {
    const optionalPropertyExample: string = this.props.optionalPropertyExample ?? Sample.defaultProps.optionalPropertyExample;
    // ...
  }

  private method2(): ReactNode {
    const optionalPropertyExample: string = this.props.optionalPropertyExample ?? Sample.defaultProps.optionalPropertyExample;
    // ...
  }

  //

  private methodN(): ReactNode {
    const optionalPropertyExample: string = this.props.optionalPropertyExample ?? Sample.defaultProps.optionalPropertyExample;
    // ...
  }


  public render(): ReactNode {
    
    const optionalPropertyExample: string = this.props.optionalPropertyExample ?? Sample.defaultProps.optionalPropertyExample;

  }

}

Is it best that possible?



from Providing of default props in class components of React + TypeScript

No comments:

Post a Comment