BaseComponent for react
Tags: react, javascript, typescript, architecture
Published: 2019-06-14 23:10:54


In most of my projects, I use inheritance from other components. And my base component is BaseComponent.

>I know about the article ["Composition vs inheritance"](https://reactjs.org/docs/composition-vs-inheritance.html), here only my point of view and some consequences from life experience.

My BaseComponent file look like this

```typescript
import * as React from 'react';
import { Subject } from 'rxjs';

export interface IBaseComponentProps {
    hidden?: boolean;
}

export interface IBaseComponent<T> {
    _error?: boolean;
    view(template?: T): JSX.Element;
    errorView(): JSX.Element;
}


export default class BaseComponent<P = any, S = any, V = any>
    extends React.Component<Readonly<P> & IBaseComponentProps, S>
    implements React.Component<Readonly<P> & IBaseComponentProps, S>, IBaseComponent<V>{

    _error: boolean = false;

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

    componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
        this._error = true;
    }

    view(template?: V): JSX.Element {
        return null;
    }

    errorView() {
        return null;
    }

    render() {
        if (this.state && this._error) {
            return this.errorView();
        }

        const props = this.props;
        if (!props.hidden) {
            return this.view();
        }
        else {
            return null;
        }
    }
}
```

Further, when creating a component instead of, 

```typescript
class MyComponent extends React.Component<IProps, IState>
```

I do (ITemplate - optional)

```typescript
class MyComponent extends BaseComponent<IProps, IState, ITemplate>
```

## And what do I get from this? 
>And from the use of inheritance in general?

Consider the example of a component of the list item

```typescript
interface ISimpleListItemProps<T> extends IBaseComponentProps {
    item: IItem & T;
    onClick?: (item: IItem & T) => void;
}

class SimpleListItem<P, S, T> extends BaseComponent<ISimpleListItemProps<T> & P>{
    constructor(props) {
        super(props);
    }

    onClick(item: IItem & T) {
        this.props.onClick(item);
    }

    view(props) {
        const item = props.item;
        return (
            <div 
            className="simple-list-item" 
            onClick={() => this.onClick(item)}>{item.title}
            </div>
        );
    }
}
```


Firstly, all components inherited from BaseComponent appear the hidden property
And if we need to hide a component we can use 

```typescript
    <SimpleListItem hidden={expression} ... />
```

instead of 

```typescript
    { expression && <SimpleListItem ... />}
```


I can also get the props in the view without using this and determine the errorView to display in case of a component crash.

---
_In my case, BaseComponent also has a stream property._

```typescript
    ...
    _error: boolean = false;
    _stream: Subject<any>;

    get stream(): Subject<any> { return this._stream }
    set stream(val: Subject<any>) { this._stream.next(val) }

    constructor(props: P) {
        super(props);
        this._stream = this.props.stream; /*!!!*/
    }
    ...
```

_That allows me to add a common messaging bus for all components in one parent, which is convenient in some cases_


_For this, there is also the BaseContainer component, in which you can enable thread creation in the constructor. The presence of such a container allows you to better control and maintain the code._

```typescript
    ...
    private _stream;

    get stream() { return this._stream };
    set stream(data) { this._stream.next(data) };

    constructor(props: P & IBaseContainerProps<T>, createStream?: boolean) {
        super(props);
        if (createStream) {
            this._stream = new Subject();
        }
    }
    ...
```
---
Return to inheritance and list items.

Now if I want to extend the functionality of the list item, I can inherit from the existing

```typescript
interface IExtendedListItemProps<T> {
    onSelect?: (item: IExtendedItem & T) => void;
    onRemove?: (item: IExtendedItem & T) => void;
}

interface IExtendedListItemState {
    selected?: boolean;
}

export default class ExtendedListItem<P, S, T> extends SimpleListItem<IExtendedListItemProps<T> & P, IExtendedListItemState | S, IExtendedItem & T>{

    constructor(props) {
        super(props);
    }

    state = { selected: this.props.item.selected || false }

    onSelect(item: IExtendedItem & T) {
        this.setState({ selected: !this.state.selected })
        this.props.onSelect && this.props.onSelect(item);
    }

    onRemove(item: IExtendedItem & T) {
        this.props.onRemove && this.props.onRemove(item);
    }

    view() {

        const props = this.props;
        const state = this.state;
        const item = props.item;

        return (
            <div className={`extended-list-item${state.selected && ' extended-list-item__selected' || ''}`}>
                <div className="extended-list-item_select" onClick={() => this.onSelect(item)}></div>
                <>
                    {super.view()}
                </>
                <div className="extended-list-item_remove" onClick={() => this.onRemove(item)}></div>
            </div>
        );
    }
}
```

The new component has both the __OnClick__ method and two new methods. I also use __super.view ()__ in a wrapper.

Create a drop-down list item that still has delete and highlight methods, but overrides the __OnClick__ event — it now expands the item

```typescript
...
    onClick(item: IExpandableItem & T) {
        this.toggle()
        this.stream.next(item);
    }

    view(template?: { body?: JSX.Element, footer?: JSX.Element }) {

        const props = this.props;
        const state = this.state;
        const item = props.item;

        const footer = template && template.footer || <div className="simple-footer">[---]</div>
        const body = template && template.body || item.body;

        return (
            <div className={`expandable-list-item${state.selected && ' expandable-list-item__selected' || ''}`}>
                <div className={"expandable-list-item_head"}>
                    {super.view()}
                </div>
                {
                    state.expanded && <div className="expandable-list-item_body">{body}</div>
                }
                {footer}
            </div>
        );
    }
...
```

Now I use the __template__ attribute functions __view__ and allows me to very briefly create a new component with a new footer

```typescript
export default class ExtendedExpandableListItem
    extends ExpandableListItem<IExtendedExpandableListItemProps, IExtendedExpandableListItemState, IExtendedExpandableItem>{
    view() {
        const item = this.props.item;
        const footer = <div className="special-footer">{item.footer}</div>;
        return super.view({ footer });
    }
}
```

## Conclusion


In some cases, inheritance provides an obvious advantage in the code before HOC or the composition of components. The readability of the code increases.
I also noticed that the use of inheritance can reduce the size of the final bundle of the application, since inheritance turns the webpack into prototype inheritance, and the HOC and composition of the components turn into new closure functions


Also, the use of inheritance from generic components like BaseComponent allows you to keep common logic in one place and, if necessary, redefine its behavior.

Thanks!

[my previous article](https://dev.to/tetragius/my-warm-state-management-library-for-react-based-on-proxy-api-23nd)