Skip to content

Home

Mapping data structures to React components

Have you ever wanted to quickly transform an array into a table or an object into a tree view? One of the benefits of React is that you can easily map data structures to components, even customizing them to your needs.

Data list

The simplest mapping is an array of primitives to a list of elements. All you really need is Array.prototype.map() to render each item as a <li> element. In addition to that, you should use a key prop to help React identify each item. Finally, wrap the result in an <ol> or <ul> element, depending on whether you want an ordered or unordered list.

const DataList = ({ isOrdered = false, data }) => {
  const list = data.map((val, i) => <li key={`${i}_${val}`}>{val}</li>);
  return isOrdered ? <ol>{list}</ol> : <ul>{list}</ul>;
};

const names = ['John', 'Paul', 'Mary'];

ReactDOM.createRoot(document.getElementById('root')).render(
  <>
    <DataList data={names} />
    <DataList data={names} isOrdered />
  </>
);

Data table

A very similar use-case is mapping an array of primitives to a table. The process is almost identical, but this time you render each item as a <tr> element with two <td> children. The first <td> contains the index of the item, while the second one contains the item itself.

const DataTable = ({ data }) => {
  return (
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Value</th>
        </tr>
      </thead>
      <tbody>
        {data.map((val, i) => (
          <tr key={`${i}_${val}`}>
            <td>{i}</td>
            <td>{val}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

const people = ['John', 'Jesse'];

ReactDOM.createRoot(document.getElementById('root')).render(
  <DataTable data={people} />
);

Object table view

For more complex structures, such as arrays of objects, you'll need a more sophisticated component. Instead of rendering each item as a <tr> element, you'll need to render each object as a <tr> element with a <td> for each key in the object. This way, you can create a table with rows dynamically created from an array of objects and a list of property names.

To get the keys and iterate over them, you'll combine Object.keys(), Array.prototype.filter(), Array.prototype.includes(), and Array.prototype.reduce(). This will produce a filteredData array, containing all objects with the keys specified in propertyNames.

❗️ Caution

This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in propertyNames.

const MappedTable = ({ data, propertyNames }) => {
  let filteredData = data.map(v =>
    Object.keys(v)
      .filter(k => propertyNames.includes(k))
      .reduce((acc, key) => ((acc[key] = v[key]), acc), {})
  );
  return (
    <table>
      <thead>
        <tr>
          {propertyNames.map(val => (
            <th key={`h_${val}`}>{val}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {filteredData.map((val, i) => (
          <tr key={`i_${i}`}>
            {propertyNames.map(p => (
              <td key={`i_${i}_${p}`}>{val[p]}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
};

const people = [
  { name: 'John', surname: 'Smith', age: 42 },
  { name: 'Adam', surname: 'Smith', gender: 'male' }
];
const propertyNames = ['name', 'surname', 'age'];

ReactDOM.createRoot(document.getElementById('root')).render(
  <MappedTable data={people} propertyNames={propertyNames} />
);

Expandable object tree view

Finally, you can create a tree view component that can display nested objects. This component uses recursion to render nested objects as nested <TreeView> components. The TreeView component takes a data prop, which is an object to render, and an optional name prop, which is the name of the object.

Using the value of the isToggled state variable, the component can toggle the visibility of the nested object. The isParentToggled prop is used to pass the state of the parent object to the child object, allowing the child object to be toggled when the parent object is toggled.

For each key in the object, the component checks if the value is an object. If it is, it renders a nested <TreeView> component. Otherwise, it renders a <p> element with the key and value of the object.

.tree-element {
  margin: 0 0 0 4px;
  position: relative;
}

.tree-element.is-child {
  margin-left: 16px;
}

div.tree-element::before {
  content: '';
  position: absolute;
  top: 24px;
  left: 1px;
  height: calc(100% - 48px);
  border-left: 1px solid gray;
}

p.tree-element {
  margin-left: 16px;
}

.toggler {
  position: absolute;
  top: 10px;
  left: 0px;
  width: 0;
  height: 0;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  border-left: 5px solid gray;
  cursor: pointer;
}

.toggler.closed {
  transform: rotate(90deg);
}

.collapsed {
  display: none;
}
const TreeView = ({
  data,
  toggled = true,
  name = null,
  isLast = true,
  isChildElement = false,
  isParentToggled = true
}) => {
  const [isToggled, setIsToggled] = React.useState(toggled);
  const isDataArray = Array.isArray(data);

  return (
    <div
      className={`tree-element ${isParentToggled && 'collapsed'} ${
        isChildElement && 'is-child'
      }`}
    >
      <span
        className={isToggled ? 'toggler' : 'toggler closed'}
        onClick={() => setIsToggled(!isToggled)}
      />
      {name ? <strong>&nbsp;&nbsp;{name}: </strong> : <span>&nbsp;&nbsp;</span>}
      {isDataArray ? '[' : '{'}
      {!isToggled && '...'}
      {Object.keys(data).map((v, i, a) =>
        typeof data[v] === 'object' ? (
          <TreeView
            key={`${name}-${v}-${i}`}
            data={data[v]}
            isLast={i === a.length - 1}
            name={isDataArray ? null : v}
            isChildElement
            isParentToggled={isParentToggled && isToggled}
          />
        ) : (
          <p
            key={`${name}-${v}-${i}`}
            className={isToggled ? 'tree-element' : 'tree-element collapsed'}
          >
            {isDataArray ? '' : <strong>{v}: </strong>}
            {data[v]}
            {i === a.length - 1 ? '' : ','}
          </p>
        )
      )}
      {isDataArray ? ']' : '}'}
      {!isLast ? ',' : ''}
    </div>
  );
};

const data = {
  lorem: {
    ipsum: 'dolor sit',
    amet: {
      consectetur: 'adipiscing',
      elit: [
        'duis',
        'vitae',
        {
          semper: 'orci'
        },
        {
          est: 'sed ornare'
        },
        'etiam',
        ['laoreet', 'tincidunt'],
        ['vestibulum', 'ante']
      ]
    },
    ipsum: 'primis'
  }
};

ReactDOM.createRoot(document.getElementById('root')).render(
  <TreeView data={data} name="data" />
);

More like this

Start typing a keyphrase to see matching snippets.