8
\$\begingroup\$

In React, the render method must have one and exactly one child (although that child may have many more children). As a result, it becomes difficult to render elements that intrinsically need multiple elements as children without wrapping them in an unsemantic - and in the case of <dl>, forbidden - <div> element.

As I result, I wrote the following code to help generate a definition list from a model. I'd like to know what you guys think. Its being used in my company's application at the moment and I was wondering if there was anything I could improve on.

'use strict'
import React from 'react'
import createFragment from 'react-addons-create-fragment'
import isArray from 'lodash.isarray'

/**
 * Creates a <dl> element using React from the given map.
 * @param  {Map<string, any|Array<any>>} map A map of name-value pairs. The keys of the map are used as labels for definitions, so they should be legible. The values are used as the value of the definition. If there are multiple values to a definition (i.e, an array), then multiple <dd> elements will be created for the resultant <dt>.
 * @return {ReactElement} A React element encapsulating the <dl>.
 */
export default function createDefinitionList(map) {
  const labels = Object.keys(map)

  return <dl>
    {labels.map((label) => createDefinitionFragment(label, map[label]))}
  </dl>
}

/**
 * Creates a <dt> with associated <dd>s inside of a React fragment. Fragments are keyed.
 * @param  {string} label A human-readable description of the definition
 * @param  {any|Array<any>} value a value, or a set of values. If given a set of values, multiple <dd> elements will be generated; these elements will be keyed using their array index.
 * @return {ReactElement} A definition list in React element form.
 */
function createDefinitionFragment(label, value) {
  return createFragment({
    definition: <dt>{label}</dt>,
    value: createDefinitionValueFragments(value)
  })
}


/**
 * Creates a React <dd> fragment for the given value. If value is an array, a fragment will be created which encapsulates all values.
 * @param  {any|Array<any>} value A singular value will return a react fragment containing just the one value encapsulated in a <dd>. Otherwise, a Fragment will be returned that contains many <dd>.
 * @return {ReactElement} A React fragment contain the definition values
 */
function createDefinitionValueFragments(value) {
  if (isArray(value)) {
    return value.map(function (val) {
      return <dd>{val}</dd>
    })
  }

  return <dd>{value}</dd>
}

Here is an example of the usage. This is basically how we use it now, except this model is retrieved via Ajax, and that's a lot of code for me to paste in a CR question. So, for sake of example, here's a finished call from one of those AJAX calls.

// Example of usage
const keyPerformanceIndicators = {
  'Calls': 0,
  'Tickets': 1,
  'Demos': [0, 10]
}

const definitionList = createDefinitionList(keyPerformanceIndicators)
React.render(definitionList, document.body)

The expected output ends up looking like this (data-reactid omitted for brevity)

<dl>
  <dt>Calls</dt>
  <dd>0</dd>
  <dt>Tickets</dt>
  <dd>1</dd>
  <dt>Demos</dt>
  <dd>0</dd>
  <dd>10</dd>
</dl>

I intend to release this as a general-purpose library as this is a niche I don't think has yet been filled in React (surprisingly). I'd like any suggestions on improving it before doing so.

\$\endgroup\$

2 Answers 2

10
\$\begingroup\$

as of React 16 version you can use

<React.Fragment>
    <dt>Term</dt>
    <dd>Definition</dd>
</React.Fragment>

React.Fragment will not render any DOM, but it'll act as a container. You can also use a short hand for React.Fragment as <>

<>
    <dt>Term</dt>
    <dd>Definition</dd>
</>

It works in the same way, except that it doesn't support any attributes or key.

https://reactjs.org/docs/fragments.html

\$\endgroup\$
1
  • \$\begingroup\$ React fragments do support the 'key' attribute: <React.Fragment key={item._id}>, which is required. \$\endgroup\$ Commented Apr 15, 2020 at 12:21
4
\$\begingroup\$

2 suggestions. There can be many terms to one definition. So it's not general-purpose. Naming - use "term" and "definition" instead of "definition" and "value" to be similar to html elements.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.