When I click on add and a modal appears then I can fill that and everything is okay. But when I open a modal by clicking on a newly created data and then when I click the save button then maximum call stack exceed appears. I saw that when I'm changing subDpFormData state then it is okay, but as soon as I click on the save button somehow the subDpFormData is changed to a infinity nested object. How can I solve that?
const dpTypes = [
'ENUM',
'STRING',
'FLOAT',
'INTEGER',
'BOOLEAN',
'DATE',
'IMAGE',
'FILE_FIELD',
'DIVISION',
'DISTRICT',
'UPAZILLA',
'UNION',
'ADDRESS',
'EMAIL',
'AUDIO'
]
const mainInitialData = {
"name": "",
"type": "",
"label": "",
"description": "",
"dependent_child": null,
"dependent_parent": null,
"min_value": null,
"max_value": null,
"options": [],
"regex": null,
"is_in_progress": false,
"is_single_valued": false,
"primary_dp_list": [],
"sub_data_point_list": []
}
const initialData = {
"name": "test",
"type": "",
"label": "",
"description": "",
"dependent_child": null,
"dependent_parent": null,
"min_value": null,
"max_value": null,
"options": [],
"regex": null,
"is_in_progress": false,
"is_single_valued": false,
"primary_dp_list": [],
"sub_data_point_list": [
]
}
const DpFormFields = ({addDatapoint}) => {
const {datapoints} = useContext(AppContext)
const [modalPath, setModalPath] = useState([]);
const [openModal, setOpenModal] = useState(false)
const [subDpFormData, setSubDpFormData] = useState(mainInitialData);
const dpKeys = Object.keys(datapoints || {})
const dataMap = [
{name: 'name', label: 'Name', size: '12', fieldType: formFieldTypes.text, value: '', required: true},
{name: 'label', label: 'Label', size: '12', fieldType: formFieldTypes.text, value: '', required: true},
{
name: 'description',
label: 'Description',
size: '12',
fieldType: formFieldTypes.text,
value: '',
required: true
},
{
name: 'type',
label: 'Type',
size: '12',
fieldType: formFieldTypes.enum,
options: dpTypes,
value: '',
required: true
},
{name: 'options', label: 'Options', size: '12', fieldType: formFieldTypes.multipleStirng, value: ""},
{name: 'min_value', label: 'Min value', size: '6', fieldType: formFieldTypes.number, value: ""},
{name: 'max_value', label: 'Max value', size: '6', fieldType: formFieldTypes.number, value: ""},
{name: 'regex', label: 'Regex', size: '12', fieldType: formFieldTypes.text, value: ""},
{
name: 'dependent_parent',
label: 'Dependent parent',
size: '12',
fieldType: formFieldTypes.dpFinder,
value: "",
options: dpKeys
},
{
name: 'dependent_child',
label: 'Dependent child',
size: '12',
fieldType: formFieldTypes.dpFinder,
value: "",
options: dpKeys
},
{name: 'primary_dp_list', label: 'Primary datapoint list', size: '6'},
{name: 'sub_data_point_list', label: 'Sub datapoint list', size: '6'},
{name: 'is_in_progress', label: 'Is in progress', size: '12', fieldType: formFieldTypes.checkbox, value: ""},
{
name: 'is_single_valued',
label: 'Is single valued',
size: '12',
fieldType: formFieldTypes.checkbox,
value: ""
},
]
const [dpFormData, setDpFormData] = useState(initialData);
const handleFieldValueChange = (fieldName, value) => {
if (fieldName === 'name') {
value = handleize(value);
}
setDpFormData((prevState) => ({
...prevState,
[fieldName]: value,
}));
};
console.log(subDpFormData)
const handleSubDpFieldValueChange = (fieldName, value) => {
if (fieldName === 'name') {
value = handleize(value);
}
setSubDpFormData((prevState) => ({
...prevState,
[fieldName]: value,
}));
};
const handleOpenModal = (path) => {
setModalPath(path);
setOpenModal(true);
// setSubDpFormData({...mainInitialData})
};
const handleCloseModal = () => {
setModalPath([]);
// setSubDpFormData({...mainInitialData})
setOpenModal(false);
};
const handleAddSubDataPoint = (e,newSubDataPoint) => {
// e.preventDefault()
console.log(newSubDataPoint,'clicked')
const updatedFormData = {...dpFormData};
// const newSubDataPoint = {...subDpFormData};
let currentLevel = updatedFormData;
let currentPath = modalPath.slice();
while (currentPath.length >= 1) {
const key = currentPath.shift();
currentLevel = currentLevel.sub_data_point_list[key];
}
currentLevel.sub_data_point_list.push(newSubDataPoint)
setDpFormData(updatedFormData);
handleCloseModal();
};
const handleDeleteSubDataPoint = (pathToDelete) => {
const updatedFormData = {...dpFormData};
let currentLevel = updatedFormData;
let currentPath = [...pathToDelete];
while (currentPath.length > 1) {
const key = currentPath.shift();
currentLevel = currentLevel.sub_data_point_list[key];
}
const parentLevel = currentLevel;
const indexToRemove = currentPath[0];
if (parentLevel && parentLevel.sub_data_point_list) {
parentLevel.sub_data_point_list.splice(indexToRemove, 1);
setDpFormData(updatedFormData);
}
// handleCloseModal();
};
const renderSubDataPoints = (subDataPoints, path = []) => {
return subDataPoints.map((subDp, index) => {
const newPath = [...path, index];
return (
<div key={index}>
<Chip
label={subDp.name}
onClick={() => handleOpenModal(newPath)}
onDelete={() => handleDeleteSubDataPoint(newPath)}
deleteIcon={<Delete className='text-danger'/>}
variant="outlined"
className={`rounded w- d-flex justify-content-between ${
path.length > 0 ? 'ml-2' : ''
}`}
/>
{subDp.sub_data_point_list.length > 0 && (
<div className={`pl-4 ${path.length > 0 ? 'ml-2' : ''}`}>
{renderSubDataPoints(subDp.sub_data_point_list, newPath)}
</div>
)}
</div>
);
});
};
return (
<Fragment>
<Card className='bg-light'>
<CardHeader title={dpFormData['name'] || "New Datapoint's name"}/>
<CardContent>
<Grid container spacing={3} className='mb-5'>
{dataMap.map((field, i) => {
if (field.fieldType) {
return (
<Grid item xs={parseInt(field.size)} key={i}>
<Input
name={field.name}
label={field.label}
value={dpFormData[field.name] || ''}
type={field.fieldType}
onChange={(value) => handleFieldValueChange(field.name, value)}
options={field.options}
required={field.required}
/>
</Grid>
)
} else return (
<Grid item xs={parseInt(field.size)} key={i}>
<Card>
<CardContent>
<div className="d-flex justify-content-between align-items-center mb-2">
<p className='text-20 mb-2'>{field.label}</p>
<Button variant="outlined" startIcon={<Add/>}
onClick={() => handleOpenModal([])}>
Add
</Button>
</div>
<div className="pl-4">
{renderSubDataPoints(dpFormData.sub_data_point_list)}
</div>
</CardContent>
</Card>
</Grid>
)
})}
</Grid>
<CardActions className='px-3 d-flex justify-content-end'>
<Button type="submit" variant='contained' color='primary'>Save</Button>
</CardActions>
<CustomModal open={openModal} handleClose={handleCloseModal}>
<CardHeader title="Create SubDataPoint" className='tkdc-primary text-white'/>
{console.log(modalPath)}
<Card className='bg-light'>
<CardHeader title={subDpFormData['name'] || "New SubDatapoint's name"}/>
<CardContent>
<Grid container spacing={3} className='mb-5'>
{dataMap.map((field, i) => {
if (field.fieldType) {
return (
<Grid item xs={parseInt(field.size)} key={i}>
<Input
name={field.name}
label={field.label}
value={subDpFormData[field.name] || ''}
type={field.fieldType}
onChange={(value) => handleSubDpFieldValueChange(field.name, value)}
options={field.options}
required={field.required}
/>
</Grid>
)
} else return (
<Grid item xs={parseInt(field.size)} key={i}>
<Card>
<CardContent>
<div
className="d-flex justify-content-between align-items-center mb-2">
<p className='text-20 mb-2'>{field.label}</p>
</div>
<div className="pl-4">
</div>
</CardContent>
</Card>
</Grid>
)
})}
</Grid>
<CardActions className='px-3 d-flex justify-content-end'>
<Button type='button' variant='contained' color='primary'
onClick={(e) => handleAddSubDataPoint(e,subDpFormData)}>Save</Button>
</CardActions>
</CardContent>
</Card>
</CustomModal>
</CardContent>
</Card>
</Fragment>
)
}
export default withAppContext(DpFormFields)