edelagnier
edelagnier

Reputation: 1545

React/TypeScript: extending a component with additional properties

I am trying to use react to recreate my currents components (written in pure typescript) but I can't find a way to give additional props to a component extending an other.

export interface DataTableProps {
    columns: any[];
    data: any[];
}

export class DataTable extends React.Component<DataTableProps, {}> {
   render() {
       // -- I can use this.props.columns and this.props.data --
   }
}

export class AnimalTable extends DataTable {
    render() {
       // -- I would need to use a this.props.onClickFunction -- 
    }
}

My problem is that I need to give AnimalTable some props that would be irrelevant to DataTable. How can I do that ?

Upvotes: 64

Views: 124106

Answers (5)

Bogus Hawtsauce
Bogus Hawtsauce

Reputation: 483

Complete example of creating a component that you can extend off of and maintain state and props

import { Component } from "react";

// Props for the Base Component
export interface BaseComponentProps { }

// State for the Base Component
export interface BaseComponentState {
    isLoaded?: boolean
}

// The Base Component that your components can extend
export class BaseComponent<Props extends BaseComponentProps, State extends BaseComponentState, SS = any> extends Component<Props, State, SS> {

    State: BaseComponentState = {
        isLoaded: false
    }

    constructor(props: Props) {
        super(props);
    }

    componentDidMount() {
        this.setState({ isLoaded: true })
    }
}

// Props for your specialized component
export interface MainComponentProps extends BaseComponentProps {

}

// State for your specialized component
export interface MainComponentState extends BaseComponentState {
    CanRead: boolean
}

// Your component which now extends the BaseComponent
export class MainComponent extends BaseComponent<MainComponentProps, MainComponentState> {
    state: MainComponentState = {
        CanRead: false
    }

    componentDidMount() {
        super.componentDidMount();

        if (this.state.isLoaded) {
            this.setState({ CanRead: true })
        }
    }
}

Upvotes: 3

anthumchris
anthumchris

Reputation: 9072

For those who need, base classes can declare required/abstract methods that all instances must implement:

import { Component } from 'react'


abstract class TestComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> {
  abstract test(): string
}


type Props = {
  first: string,
  last: string,
}

type State = {
  fullName: string,
}

class MyTest extends TestComponent<Props, State> {
  constructor(props: Props) {
    super(props)
    this.state = {
      fullName: `${props.first} ${props.last}`
    }
  }

  test() {
    const { fullName } = this.state
    return fullName
  }
}

Upvotes: 5

ironic
ironic

Reputation: 8939

as a rule of thumb it is probably better to avoid inheritance. luckily TS and react are great tools allowing that (unlike c# for example, where inheritance often saves you a bunch of boilerplate)

export interface DataTableProps {
    columns: any[];
    data: any[];
}

export class DataTable extends React.Component<DataTableProps, {}> {
   render() {
       // -- I can use this.props.columns and this.props.data --
   }
}

export type AnimalTableProps = DataTableProps & {
    onClickFunction: () => void;
};

export class AnimalTable extends React.Component<AnimalTableProps, {}> {
    render() {
        const {onClickFunction, ...tableProps} = this.props;
        // use onClickFunction however you need it
        return <DataTable {...tableProps}></DataTable>
    }
}

Upvotes: 9

radzserg
radzserg

Reputation: 1396

The most elegant solution that I found (without extra generic class) is

interface IBaseProps {
    name: string;
}

class Base<P> extends React.Component<P & IBaseProps, {}>{

}

interface IChildProps extends IBaseProps {
    id: number;
}

class Child extends Base<IChildProps> {
    render(): JSX.Element {
        return (
            <div>
                {this.props.id}
                {this.props.name} 
            </div>
        );
    }
}

Upvotes: 6

Nitzan Tomer
Nitzan Tomer

Reputation: 164129

You'll need to make DataTable generic so that you'll be able to use an interface which extends DataTableProps:

export interface AnimalTableProps extends DataTableProps {
    onClickFunction: Function;
}

export class DataTable<T extends DataTableProps> extends React.Component<T, {}> { }

export class AnimalTable extends DataTable<AnimalTableProps> {
    render() {
        // this.props.onClickFunction should be available
    }
}

Upvotes: 87

Related Questions