My goal here is to make data driven nested forms in React. I've created a function that points to a form configuration JSON, which can contain both components and references to other configuration files, with their own components. I'm splitting it to separate files to keep things DRY and to get an easier overview (hierarchy can be about 5 levels deep, one JSON file would be rather hard to overview).
I've introduced $ref
to be able to point to another JSON configuration and $extends
for inheritance (configurations can differ dependings on context but have common properties). args
will be spread out as props to the component.
config-builder.js
function createConfiguration(configuration) {
return _.reduce(configuration.components, (result, data) => {
// if it's a reference to another configuration - fetch it - otherwise use as is
const component = data.$ref ? getComponentByReference(data) : data
return addResult(result, component.key, component.components ? { ...component, components: createConfiguration(component) } : component)
}, {})
}
function getComponentByReference(data) {
const component = _.merge({}, configMap[data.$ref], data)
const result = component.$extends ? _.merge({}, configMap[component.$extends], component) : component
// clear internal values like $ref, $extends
return _.omitBy(result, (value, key) => _.startsWith(key, '$'))
}
// use an array to wrap results to handle multiple keys of the same type on same level
function addResult(result, key, data) {
if(!result[key]) {
result[key] = [data]
} else {
result[key].push(data)
}
return result
}
foo.json
{
"key": "foo",
"type": "DefaultContainer",
"components": [
{ "key": "name", "type": "Text" },
{ "$ref": "Bar" }
]
}
bar.json
{
"key": "bar",
"type": "DefaultContainer",
"components": [
{ "key": "id", "type": "DropDown", "args": { options: [] } }
]
}