G'Day. I want to iterate over a bunch of JSON objects and turn them into React Elements. The objects look like this
"fields":
[
{
key: "testname",
"name": "testname",
"altName": "",
"visible": true,
"groupVisibility": "public",
"type": "text",
"component": "input",
"label": "Test Smart Input",
"placeholder": "Some default Value",
"required": "required",
"validated": false,
"data": []
},
{
key: "password",
"name": "password",
"altName": "",
"visible": true,
"groupVisibility": "public",
"type": "password",
"component": "input",
"label": "Test Smart Input",
"placeholder": "Password",
"required": "required",
"validated": false,
"data": []
}
]
And the code that iterates over them is quite simple. Such:
//--------------------
formFields(fieldsIn) {
const fieldsOut = []; // ARRAY of FORM ELEMENTS to return
console.log('doin fields');
for (var fieldIn in fieldsIn) { // array of FORM ELEMENT descriptions in JSON
console.log(fieldIn);
let field = React.createElement(SmartRender, // go build the React Element
fieldIn,
null); // lowest level, no children, data is in props
console.log('doin fields inside');
fieldsOut.push(field);
}
return(fieldsOut); // this ARRAY is the children of each PAGE
}
And I get the error Warning: Each child in an array or iterator should have a unique "key" prop. Any hints? Cheers
I changed the code to do this.
//--------------------
formFields(fieldsIn) {
const fieldsOut = []; // ARRAY of FORM ELEMENTS to return
console.log('doin fields');
for (var fieldIn in fieldsIn) { // array of FORM ELEMENT descriptions in JSON
console.log(fieldIn);
let field = React.createElement(SmartRender, // go build the React Element
{key: fieldsIn[fieldIn].name, fieldIn},
null); // lowest level, no children, data is in props
console.log('doin fields inside');
fieldsOut.push(field);
}
return(fieldsOut); // this ARRAY is the children of each PAGE
}
And I get the same error. I don't understand why! Fixed! Thanks for the help.
Here is the code.
//--------------------
formFields(fieldsIn) {
const fieldsOut = []; // ARRAY of FORM ELEMENTS to return
for (var fieldIn in fieldsIn) { // array of FORM ELEMENT descriptions in JSON
console.log(fieldIn);
let field = React.createElement(SmartRender, // go build the React Element
{key: fieldsIn[fieldIn].key, fieldIn},
null); // lowest level, no children, data is in props
fieldsOut.push(field);
}
return(fieldsOut); // this ARRAY is the children of each PAGE
}
//----------------------
pages(pagesIn, format) {
// I tried to do this in JSX, but no syntax I wrestled with would
// allow me to access the childred when building this with the
// BABEL transpiler. Same goes for the METHOD just above, items().
//
// This method returns an array of pages this are React elements
// this are treated as children by the smartForm.
const pagesOut = []; // array of pages to build and return
let Section = {}; // Component to fire in the build
switch(format) {
case 'accordion': {
Section = AccordionSection;
break;
}
case 'workflow': {
Section = null; // I haven't written this yet
break;
}
case 'simple': {
Section = null; // I haven't written this yet
break;
}
}
for (var pageIn in pagesIn) { // pages, any format, any number 1..N
let children = this.formFields(pagesIn[pageIn].fields); // 1..N fields, we don't know beforehand
let page = React.createElement( Section,
pagesIn[pageIn].props,
children);
pagesOut.push(page);
}
return(pagesOut); // this ARRAY is the children of each FORM
}
//--------
render() {
let formIn = this.props.form; // JSON description of FORM
let formOut = null; // contructed REACT/Javascript form
switch (formIn.format) { // what type of operation is this
case 'accordion': { // Accordion in a FORM, OK
let children = this.pages(formIn.pages,
formIn.format); // build the children
formOut = React.createElement(Accordion, // construct the parent with ALL nested CHILDREN after
{key: formIn.formName}, // just a unique key
children); // N ACCORDION pages, N2 input fields
break;
}
case 'workflow': {
let children = this.pages(formIn.pages, // build the children
formIn.format); // build the children
formOut = React.createElement(Workflow, // and create the complex sheet element
{ key: formIn.formName},
children); // N SLIDING pages, N2 input fields
break;
}
case 'simple': {
let children = this.pages(formIn.pages, // build the children
formIn.format); // build the children
formOut = React.createElement(Simple,
{ key: formIn.formName},
children); // One page, N input fields
break;
}
}
return(
<div>
<h2>SmartForm Parser</h2>
<p>"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."</p>
{formOut}
</div>
);
}
}
//-------------------------------------------------------------------------
export default SmartForm;
//----------------- EOF -------------------------------------------------
You need to add a unique key prop to your React element.
According to the React docs:
You can do it like
Why are keys necessary?
By default, when recursing on the children of a DOM node, React just iterates over both lists of children at the same time and generates a mutation whenever there’s a difference.
For example, when adding an element at the end of the children, converting between these two trees works well:
React will match the two
<li>first</li>
trees, match the two<li>second</li>
trees, and then insert the<li>third</li>
tree.If you implement it naively, inserting an element at the beginning has worse performance. For example, converting between these two trees works poorly.
That is where keys come in handy.