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.