DEV Community

Cover image for React add-on components
Mahmoud Elmahdi
Mahmoud Elmahdi

Posted on

React add-on components

React is one if not the most popular JavaScript framework out there, however it doesn’t come with helpers out of the box like other frameworks does (i.e. directives in Vue.js).

I’ll share with you in this post 4 useful and reusable react components to maximize your coding efficiency.

Conditional render

The If component, a functional component with a condition and otherwise properties.

The condition property is a pretty straightforward conditional statement. If the given condition is truthy a predefined children property will be returned or else whatever values passed into the otherwise property will be rendered or nothing at all

Usage

<If condition={flag} otherwise="render that">
  render this...
</If>
Enter fullscreen mode Exit fullscreen mode

If.js

import React from 'react';
import Proptypes from 'prop-types';

function If(props) {
  return props.condition ? props.children : props.otherwise;
}

If.propsTypes = {
  condition: Proptypes.bool,
  otherwise: Proptypes.oneOfType([
      Proptypes.arrayOf(Proptypes.node),
      Proptypes.node
  ]),
  children: Proptypes.oneOfType([
    Proptypes.arrayOf(Proptypes.node),
    Proptypes.node
  ])
};

If.defaultProps = {
  otherwise: null
};

export default If;
Enter fullscreen mode Exit fullscreen mode

am assuming you already have prop-types or whatever type checks package installed in your application.

Broken images

The Image component replaces the broken src of an image with a fallback property (image) as its default placeholder.

Usage

<Image src={pathToBrokentImage} alt="..." />
Enter fullscreen mode Exit fullscreen mode

Image.js

import React from 'react';
import Proptypes from 'prop-types';

function Image(props) {
  const { fallback, alt, ...rest } = props;
  const handleBrokenImage = (event) => event.target.src = fallback;

  return <img {...rest} alt={alt} onError={handleBrokenImage} />;
}

Image.propTypes = {
  fallback: Proptypes.string,
  alt: Proptypes.string,
};

Image.defaultProps = {
  alt: 'Default alt for a11y',
  fallback: 'path/to/default/image/or/placeholder'
};

export default Image;
Enter fullscreen mode Exit fullscreen mode
Util

I'll create a simple arrow function as a util to use in the next two components, to generate a random key for each element since we're going to iterate over a list of data to elements (to prevent any warn/error logs in the console )

const getRandomKey = () => Math.random().toString(36).substr(2, 3);
Enter fullscreen mode Exit fullscreen mode

Mapping an array to elements

The For component iterates over the of property that accepts an array of data, this could be list of strings or list objects.

Usage

const data = ['...', '...', '...'];

<For of={data} type='p' />

const anotherData = [
  {
   label: '...',
   value: '...',
  }
  {
   label: '...',
   value: '...',
  }
  {
   label: '...',
   value: '...',
  }
];

<For of={anotherData} type='li' parent='ul' iteratee='label' />
Enter fullscreen mode Exit fullscreen mode

If no iteratee property provided! the component will return the first key value of each object within the array.

For.js

import React, { PureComponent, createElement } from 'react';
import Proptypes from 'prop-types';

export default class For extends PureComponent {
  static propTypes = {
    of: Proptypes.array,
    type: Proptypes.string.isRequired,
    parent: Proptypes.string,
    iteratee: Proptypes.string,
  };

  getIteratee = (item) => {
    return item[this.props.iteratee] || item[Object.keys(item)[0]];
  };

  list = () => {
    const { of, type } = this.props;
    return of.map((item) => {
      const children = typeof item === 'object' ? this.getIteratee(item) : item;
      return createElement(type, {
        key: getRandomKey()
      }, children)
    })
  };

  children = () => {
    const { parent } = this.props;
    return parent ? createElement(parent, null, this.list()) : this.list();
  };

  render() {
    return this.props.of.length ? this.children() : null;
  }
}
Enter fullscreen mode Exit fullscreen mode

Data table

A basic Table component that renders data table with headers, body and footer.

Usage

const data = {
  headers: ['...', '...'],
  body: [
    ['...', '...'],
    ['...', '...'],  
  ],
  footer: ['...', '...'],
};

<Table {...data} />
Enter fullscreen mode Exit fullscreen mode

Table.js

you can make it more challengeable by adding more options, for example a variety of table layout and more...

import React from 'react';
import Proptypes from 'prop-types';

export default class Table extends React.PureComponent {
  static propTypes = {
    header: Proptypes.array,
    body: Proptypes.array,
    footer: Proptypes.array,
  };

  static defaultProps = {
    header: [],
    body: [],
    footer: [],
  };

  static Cells = ({ data = [], cell = 'td' }) => data.length ? (
      data.map((d) => (
          cell === 'th' ?
              <th key={`th-${getRandomKey()}`}>{d}</th> :
              <td key={`td-${getRandomKey()}`}>{d}</td>
      ))
  ) : null;

  render() {
    const { header, body, footer, ...rest } = this.props;
    const bodyRows = body.map((row) => (
        <tr key={`th-${getRandomKey()}`}>
          <Table.Cells data={row} />
        </tr>
    ));

    return (
        <table {...rest}>
          {header.length ? (
              <thead>
                <tr>
                  <Table.Cells data={header} cell="th" />
                </tr>
              </thead>
          ) : null}
          {body.length ? <tbody>{bodyRows}</tbody> : null}
          {footer.length ? (
              <tfoot>
                <tr>
                  <Table.Cells data={footer} />
                </tr>
              </tfoot>
          ) : null}
        </table>
    )
  }
}
Enter fullscreen mode Exit fullscreen mode

Demo

I’ve made a simple application to play with. It has a several sections as you can tell from the demo below. Each component has sample test, feel free to fork and play around with the code.


Feedback are welcome. If you have any suggestions or corrections to make, please do not hesitate to drop me a note/comment.

Top comments (0)