Reputation: 1545
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
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
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
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
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
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