If you are using Formik and class component the please see the following sloution:
let say I have following json on every attribute need to create one field in the create product form:
{ "name": "keychoda", "attributes": { "General_Attributes": [ { "name": "Product Type", "code": "product_code", "mandatory": "1", "type": "text", "max_length": "30", "default_value": "cellularphonecase", "Hint": "Enter Feed Product Type here", "AmazonExportHeader": "feed_product_type", "ShopifyExportHeader": "Type", "EbayExportHeader": "*Category", "GoogleExportHeader": "Google Shopping / Google Product Category" }, { "name": "Shop Keeping Unit", "code": "shop_keeping_unit", "mandatory": "1", "type": "text", "max_length": "12", "default_value": "", "Hint": "Enter Product SKU Number", "AmazonExportHeader": "item_sku", "ShopifyExportHeader": "Variant SKU", "EbayExportHeader": "CustomLabel", "GoogleExportHeader": "" }, { "name": "Brand Name", "code": "brand_name", "mandatory": "1", "type": "dropdown", "options": [ "Oxnoble Group", "Membrane" ], "max_length": "", "default_value": "Oxnoble Group", "Hint": "Select Brand Name", "AmazonExportHeader": "item_sku", "ShopifyExportHeader": "Vendor", "EbayExportHeader": "*C:Brand", "GoogleExportHeader": "" }, { "name": "Barcode", "code": "barcode", "mandatory": "1", "type": "number", "min_length": "13", "max_length": "13", "default_value": "", "Hint": "Enter EAN / GTIN HERE", "AmazonExportHeader": "external_product_id", "ShopifyExportHeader": "Variant Barcode", "EbayExportHeader": "Product:EAN", "GoogleExportHeader": "" }, { "name": "Barcode Type", "code": "barcode_type", "mandatory": "1", "type": "dropdown", "options": [ "GTIN", "EAN", "UPC", "ASIN" ], "min_length": "", "max_length": "", "default_value": "", "Hint": "Select Barcode Type", "AmazonExportHeader": "external_product_id_type", "ShopifyExportHeader": "", "EbayExportHeader": "C:Type", "GoogleExportHeader": "" }, { "name": "condition_type", "code": "condition_type", "mandatory": "1", "type": "dropdown", "options": [ "New", "Used - Acceptable", "Used - Good", "Used - Like New", "Used - Very Good" ], "min_length": "", "max_length": "", "default_value": "", "Hint": "condition_type", "AmazonExportHeader": "condition_type", "ShopifyExportHeader": "", "EbayExportHeader": "", "GoogleExportHeader": "" }, { "name": "condition_note", "code": "condition_note", "mandatory": "1", "type": "text", "min_length": "", "max_length": "", "default_value": "", "Hint": "condition_note", "AmazonExportHeader": "condition_note", "ShopifyExportHeader": "", "EbayExportHeader": "", "GoogleExportHeader": "" } ], "Variation_Attributes": [ ], "Dimentions_Attributes": [ { "name": "image1", "code": "image1", "mandatory": "1", "type": "file", "default_value": "", "Hint": "condition_type", "AmazonExportHeader": "condition_type", "ShopifyExportHeader": "", "EbayExportHeader": "", "GoogleExportHeader": "" }, { "name": "image2", "code": "image2", "mandatory": "1", "type": "file", "default_value": "", "Hint": "condition_type", "AmazonExportHeader": "condition_type", "ShopifyExportHeader": "", "EbayExportHeader": "", "GoogleExportHeader": "" }, { "name": "Short Description", "code": "short_description", "mandatory": "0", "type": "textarea", "min_length": "", "max_length": "", "default_value": "", "Hint": "condition_note", "AmazonExportHeader": "condition_note", "ShopifyExportHeader": "", "EbayExportHeader": "", "GoogleExportHeader": "" } ] } }
createproduct.js
import React from 'react'; import { connect } from 'react-redux'; import { axiosInstance } from "../../network/apis/index"; import { END_POINTS } from "../../utils/Constants"; import { Link } from "react-router-dom"; import DashboardTemplate from '../../components/DashboardTemplate'; import AttributeSet from '../../components/AttributeSet'; import { Formik, Form, Field } from 'formik'; import { ToastContainer, toast } from 'react-toastify'; import { Btn } from '../../components/Controls/Button/Button'; class CreateProduct extends React.Component { constructor(props) { super(props); this.state = { redirect: false, catId: "", attributes : {}, initialvalues: {} } } componentDidMount() { const params = new URLSearchParams(window.location.search); let cat; this.props.Categories.some( (item) => { return (item._id === params.get('catid')) ? cat = item : ""; }); let iniValues = {}; const obj = Object.values(cat.attributes); obj.map( entries => { if(entries.length > 0) { entries.map(entry => { let name = "\""+ entry.code + "\""; if(entry.type == "file") { iniValues[name] = null; } else { iniValues[name] = ""; } }) } }); this.setState( { catId: cat._id, attributes: cat.attributes, initialvalues: iniValues } ); } handleProduct = async (values) => { console.log(values); let newProductPayload = { name: "", category_id: "", attributes: {} } let combined = []; Object.entries(this.state.attributes).map( attributes => { combined = combined.concat(...attributes[1]); }); //console.log(combined); this.buildProductPayload(values, newProductPayload, combined); console.log(newProductPayload); let url = END_POINTS.createProduct; try { const response = await axiosInstance.post(url,newProductPayload); console.log(response); toast.success("Product saved"); this.setState({redirect: true}) } catch (error) { console.log(error); toast.error("Unable to save Product"); } } buildProductPayload(values, newProductPayload, combined) { Object.entries(values).map((value) => { if (value[0] == "name") { newProductPayload.name = value[1]; newProductPayload.category_id = this.state.catId; } else { for (let i = 0; i < combined.length; i++) { if (combined[i].code == value[0]) { newProductPayload.attributes[value[0]] = { value: value[1], AmazonExportHeader: combined[i].AmazonExportHeader, EbayExportHeader: combined[i].EbayExportHeader, GoogleExportHeader: combined[i].GoogleExportHeader, ShopifyExportHeader: combined[i].ShopifyExportHeader }; break; } } } }); } render() { console.log(this.state.initialvalues,this.state.attributes); return ( <DashboardTemplate> <div className="content_header_bar"> <div className="content_header_bar_child content_bar_left"> <h3>Create Product</h3> </div> <div className="content_header_bar_child content_bar_right"> <Link to="/category-select" className="gray-button">Back</Link> </div> </div> <div className="create_product_main"> <div className="create_product_inner"> <ToastContainer /> <Formik initialValues={ this.state.initialvalues } onSubmit={(values, { setSubmitting }) => { setTimeout(() => { setSubmitting(false); this.handleProduct(values); }, 500); }} > {({ submitForm, isSubmitting, setFieldValue, values }) => ( <Form> <div className="create_product_item"> <AttributeSet title="Product Name" fieldname="name" type="text" required="1" /> </div> { Object.keys(this.state.attributes).map((attribute,i) => { return( <div key={i} className="create_product_item"> <div className="attribute_set_name">{ attribute.replace("_", " ") }</div> {this.state.attributes[attribute].length > 0 && this.state.attributes[attribute].map((entry,index) => { return( <AttributeSet key={index} title={ entry.name } fieldname={entry.code} type={ entry.type } options={entry.options} required={entry.mandatory} sfv={setFieldValue} values={values} /> ) })} </div> ) })} <Btn className="create_button create_product_button" handleClick={submitForm} text="Create" /> </Form> )} </Formik> </div> </div> </DashboardTemplate> ) } } const mapStateToProps = ({Categories}) => { return { Categories: Categories } } export default connect(mapStateToProps,null)(CreateProduct);
and child component which is responsible to return dynamic form is Attributeset.js
import React, {Component} from "react"; import { Field } from 'formik'; import PlaceholderImage from '../../assets/images/image-placeholder.jpg'; class AttributeSet extends Component { constructor(props) { super(props) this.state = { file: PlaceholderImage } this.handleChange = this.handleChange.bind(this) } handleChange(event) { //console.log(this.props.sfv); const reader = new FileReader(); reader.onload = () => { if (reader.readyState === 2) { this.setState({file: reader.result}) } } reader.readAsDataURL(event.target.files[0]); //console.log(this.props.values); this.props.sfv(this.props.fieldname, event.currentTarget.files[0]); //console.log(this.props.values); } render() { let column = 'four_column'; var inputValue = ''; switch (this.props.type) { case 'textarea': column = 'full_width'; inputValue = <Field className="attribute_input" name={this.props.fieldname} type="textarea" rows="4" cols="50" placeholder={ this.props.title } /> break; case 'file': inputValue = <> <img src={ this.state.file } alt="" className="image_preview"/> <input className="attribute_input" name={this.props.fieldname} type="file" onChange={this.handleChange} /> </> break; case 'dropdown': inputValue = <Field as="select" name={this.props.fieldname} className="attribute_input" > <option value=""></option> { this.props.options && this.props.options.map( (option,i) => { return ( <option key={i} value={ option }>{ option }</option> ); })} </Field> break; default: inputValue = <Field className="attribute_input" name={this.props.fieldname} type="text" placeholder={ this.props.title } /> break; } return ( <div className={ `attibute_set ${column}` }> <div className="attibute_name"> { this.props.title }</div> <div className="attribute_input"> {inputValue} <div className="attribute_field_error"></div> </div> </div> ); } } export default AttributeSet;