I am getting an intermittent error on call stack exceeded i have applied various validation which are just reducing them to some extent but not able to figure out the core of this issue
using recursion to add data to tree view component of material-UI in react.
main recursive function
const getRegionTree = (parentNodeId, parentLocationName, position) => {
if (position >= 7)
return;
let parentKey = generateKey(parentNodeId, parentLocationName);
if (Object.keys(manageRegionData).length > 0)
return (
manageRegionData[parentKey] != undefined ?
manageRegionData[parentKey].nodes.map((childLocation) => {
let childKey = generateKey(childLocation.nodeId, childLocation.name);
return (
getTreeItem(
childLocation.nodeId,
childLocation.name,
childLocation,
childLocation.position,
(
loaderId === childKey ?
<CircularProgress className={classes.treeItemApiLoader} size={20} />
:
// in case key and data not present - no api call made
manageRegionData[childKey] === undefined ?
<React.Fragment /> :
// in case key present and not the data as api didn't receive it
manageRegionData[childKey] === null ?
<TreeItem endIcon={<HorizontalRuleIcon className={classes.endIconLine} />} classes={{ label: classes.treeItemLabelText }} nodeId="-1" label="no data available" /> :
getRegionTree(childLocation.nodeId, childLocation.name, childLocation.position)
),
//dummy only for dev
increaseNodeId
)
)
})
: <React.Fragment />
)
else
return <React.Fragment />
}
manageRegion
{
"13006-Malta": {
"nodes": [
{
"nodeId": 13446,
"name": "Birzebbuga",
"path": "World->Europe->Southern Europe->Malta->Birzebbuga",
"position": 5,
"positionName": "State"
}
]
},
"12751-World": {
"nodes": [
{
"nodeId": 12752,
"name": "Americas",
"path": "World->Americas",
"position": 2,
"positionName": "Continent"
},
{
"nodeId": 12753,
"name": "Asia",
"path": "World->Asia",
"position": 2,
"positionName": "Continent"
},
{
"nodeId": 12754,
"name": "Oceania",
"path": "World->Oceania",
"position": 2,
"positionName": "Continent"
},
{
"nodeId": 12755,
"name": "Europe",
"path": "World->Europe",
"position": 2,
"positionName": "Continent"
},
{
"nodeId": 12756,
"name": "Africa",
"path": "World->Africa",
"position": 2,
"positionName": "Continent"
}
]
},
"12752-Americas": {
"nodes": [
{
"nodeId": 12757,
"name": "Caribbean",
"path": "World->Americas->Caribbean",
"position": 3,
"positionName": "Sub Continent"
},
{
"nodeId": 12758,
"name": "Central America",
"path": "World->Americas->Central America",
"position": 3,
"positionName": "Sub Continent"
},
{
"nodeId": 12759,
"name": "Northern America",
"path": "World->Americas->Northern America",
"position": 3,
"positionName": "Sub Continent"
},
{
"nodeId": 12760,
"name": "Latin America and the Caribbean",
"path": "World->Americas->Latin America and the Caribbean",
"position": 3,
"positionName": "Sub Continent"
},
{
"nodeId": 12761,
"name": "South America",
"path": "World->Americas->South America",
"position": 3,
"positionName": "Sub Continent"
}
]
},
"12759-Northern America": {
"nodes": [
{
"nodeId": 12941,
"name": "Canada",
"path": "World->Americas->Northern America->Canada",
"position": 4,
"positionName": "Country"
},
{
"nodeId": 12942,
"name": "Greenland",
"path": "World->Americas->Northern America->Greenland",
"position": 4,
"positionName": "Country"
},
{
"nodeId": 12943,
"name": "Saint Pierre and Miquelon",
"path": "World->Americas->Northern America->Saint Pierre and Miquelon",
"position": 4,
"positionName": "Country"
},
{
"nodeId": 12944,
"name": "United States",
"path": "World->Americas->Northern America->United States",
"position": 4,
"positionName": "Country"
},
{
"nodeId": 12945,
"name": "Bermuda",
"path": "World->Americas->Northern America->Bermuda",
"position": 4,
"positionName": "Country"
}
]
},
"12943-Saint Pierre and Miquelon": null,
"12945-Bermuda": {
"nodes": [
{
"nodeId": 13324,
"name": "Hamilton City",
"path": "World->Americas->Northern America->Bermuda->Hamilton City",
"position": 5,
"positionName": "State"
},
{
"nodeId": 13325,
"name": "Pembroke Parish",
"path": "World->Americas->Northern America->Bermuda->Pembroke Parish",
"position": 5,
"positionName": "State"
}
]
},
"12760-Latin America and the Caribbean": null
}
full length code
import React, { Component, useEffect, useState } from 'react';
import { Grid, Button, Dialog, DialogActions, DialogTitle, DialogContent } from '@material-ui/core';
import TreeView from '@material-ui/lab/TreeView';
import TreeItem from '@material-ui/lab/TreeItem';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
import CircularProgress from '@mui/material/CircularProgress';
import CircleIcon from '@mui/icons-material/Circle';
import { useStyles } from './ManageRegionStyles';
import { useDispatch, useSelector } from 'react-redux';
import { ClearManageRegionData, HandleManageRegionDataAddition, HandleSelectedRegionsAddition, increaseJ } from '../../../store/Admin/AdminActions';
import * as Constants from '../../../Common/Constants';
import * as TripServices from '../../../Services/TripServices';
import ApiHandlingHOC from '../../ApiHandlingHOC';
import HorizontalRuleIcon from '@mui/icons-material/HorizontalRule';
const ManageRegion = (props) => {
const classes = useStyles();
const dispatch = useDispatch();
const manageRegionData = useSelector(state => state && state.admin && state.admin.manageRegionData);
const selectedRegions = useSelector(state => state && state.admin && state.admin.selectedRegions);
const [selectedRegionsLocalState, setSelectedRegionsLocalState] = useState({});
//dummy only for development
const jValue = useSelector(state => state.admin.j);
const [loaderId, setLoaderId] = useState("");
useEffect(() => {
if (props.open && Object.keys(selectedRegions).length > 0)
setSelectedRegionsLocalState(selectedRegions);
}, [props.open]);
useEffect(() => {
// console.log('local sr use effect', selectedRegionsLocalState);
}, [selectedRegionsLocalState])
useEffect(() => {
console.log('mr ', manageRegionData);
}, [manageRegionData])
console.log(manageRegionData);
const closeDialog = () => {
handleClearManageRegionData();
props.handleClose();
setSelectedRegionsLocalState({});
setLoaderId("");
}
const generateKey = (nodeId, name) => {
return String(nodeId) + "-" + String(name);
}
const getDialogHeader = () => {
return (
<Grid container>
<Grid item>
Add Region(s)
</Grid>
</Grid>
)
}
const updateManageRegionData = (nodeId, locationName, childNodesData) => {
dispatch(HandleManageRegionDataAddition(String(nodeId), locationName, childNodesData));
}
const handleClearManageRegionData = () => {
dispatch(ClearManageRegionData());
}
//dummy function only while development
const increaseNodeId = () => {
let data = getData();
let locations = data.nodes;
for (let i = 0; i < locations.length; i++) {
locations[i].nodeId += jValue;
}
dispatch(increaseJ());
return data;
}
//dummy function only while development
const getData = () => {
return ({
"nodes": [
{
"nodeId": 12757,
"name": "Caribbean",
"path": "World->Americas->Caribbean",
"positionName": "Sub Continent",
"position": 3
},
{
"nodeId": 12758,
"name": "Central America",
"path": "World->Americas->Central America",
"positionName": "Sub Continent",
"position": 3
},
{
"nodeId": 12759,
"name": "Northern America",
"path": "World->Americas->Northern America",
"positionName": "Sub Continent",
"position": 3
},
{
"nodeId": 12760,
"name": "Latin America and the Caribbean",
"path": "World->Americas->Latin America and the Caribbean",
"positionName": "Sub Continent",
"position": 3
},
{
"nodeId": 12761,
"name": "South America",
"path": "World->Americas->South America",
"positionName": "Sub Continent",
"position": 3
}
],
"freshness": 0,
"dataId": null,
"language": null,
"message": null
});
}
const handleApiSuccess = (nodeId, locationName, response) => {
if (response) {
updateManageRegionData(nodeId, locationName, response);
}
}
const makeApiCall = async (nodeId, locationName) => {
let callKey = generateKey(nodeId, locationName);
setLoaderId(callKey);
try {
let response = await props.handleApiCall(TripServices.getLocationChildNodes(nodeId));
if (response)
handleApiSuccess(nodeId, locationName, response.data.payload);
else {
updateManageRegionData(nodeId, locationName, null);
}
}
catch (err) {
console.log('err' + err);
setLoaderId("");
}
setLoaderId("");
}
const getTreeItem = (nodeId, nodeName, nodeData, nodePosition, itemChildJSX, nodeChildData) => {
let clickedKey = generateKey(nodeId, nodeName);
let treeItemProps = {
onClick: () => {
if (
// node is not a facility
nodePosition < 7 &&
//previously call has not been made for this node
manageRegionData[clickedKey] == undefined &&
//previous call returned no data
manageRegionData[clickedKey] !== null
) {
makeApiCall(nodeId, nodeName);
}
// if (nodeChildData)
// updateManageRegionData(nodeId, nodeName, nodeChildData());
},
key: String(nodeId),
// labelIcon: nodePosition === 7 ? <CircleIcon /> : <></>
nodeId: String(nodeId),
label: getTreeItemLabel(nodeName, nodeData)
}
return (
nodePosition !== 7
?
<TreeItem
{...treeItemProps}
>
{itemChildJSX}
</TreeItem>
:
<TreeItem
{...treeItemProps}
endIcon={<CircleIcon className={classes.endIconCircle} />}
/>
)
}
//recursion for the region tree
const getRegionTree = (parentNodeId, parentLocationName, position) => {
if (position >= 7)
return;
let parentKey = generateKey(parentNodeId, parentLocationName);
if (Object.keys(manageRegionData).length > 0)
return (
manageRegionData[parentKey] != undefined ?
manageRegionData[parentKey].nodes.map((childLocation) => {
let childKey = generateKey(childLocation.nodeId, childLocation.name);
return (
getTreeItem(
childLocation.nodeId,
childLocation.name,
childLocation,
childLocation.position,
(
loaderId === childKey ?
<CircularProgress className={classes.treeItemApiLoader} size={20} />
:
// in case key and data not present - no api call made
manageRegionData[childKey] === undefined ?
<React.Fragment /> :
// in case key present and not the data as api didn't receive it
manageRegionData[childKey] === null ?
<TreeItem endIcon={<HorizontalRuleIcon className={classes.endIconLine} />} classes={{ label: classes.treeItemLabelText }} nodeId="-1" label="no data available" /> :
getRegionTree(childLocation.nodeId, childLocation.name, childLocation.position)
),
//dummy only for dev
increaseNodeId
)
)
})
: <React.Fragment />
)
else
return <React.Fragment />
}
const handleRegionSelectUnselect = (value, selectedKey, selected) => {
let selectedRegions = { ...selectedRegionsLocalState };
if (selected === true) {
selectedRegions[selectedKey] = value;
}
else {
delete selectedRegions[selectedKey];
}
setSelectedRegionsLocalState(selectedRegions);
}
const getTreeItemLabel = (labelText, labelData) => {
let selectedKey = generateKey(labelData.nodeId, labelText);
let isItemSelected = selectedRegionsLocalState[selectedKey];
return (
<Grid
className={classes.treeItemGrid}
style={
isItemSelected ? { color: Constants.APP_PRIMARY_COLOR, backgroundColor: "rgba(19,111,213,0.09)" }
: {}
}
container justify="space-between" >
<Grid item className={classes.treeItemLabelText}>{labelText}</Grid>
<Grid item
className={classes.selectUnSelectText}
onClick={(event) => {
handleRegionSelectUnselect(
labelData,
selectedKey,
//toggle value in case selected -> deselect item and vice versa
isItemSelected ? false : true
)
event.preventDefault();
}}>
{
(
isItemSelected ?
Constants.ClickToDeselectRegion :
Constants.ClickToSelectRegion
)
}
</Grid>
</Grid >
)
}
const getDialogContent = () => {
return (
<Grid container>
<TreeView
classes={{
root: classes.treeViewRootCustom
}}
defaultCollapseIcon={<ArrowDropDownIcon />}
defaultExpandIcon={<ArrowRightIcon />}
sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }}
>
{
getTreeItem(
Constants.RegionWorldNodeId,
Constants.RegionWorldLabel,
Constants.RegionWorldData,
Constants.RegionWorldPosition,
getRegionTree(
Constants.RegionWorldNodeId,
Constants.RegionWorldLabel,
Constants.RegionWorldPosition
),
//dummy only for dev
getData
)
}
{/* <TreeItem
nodeId={String(Constants.RegionWorldNodeId)}
label={
getTreeItemLabel(
Constants.RegionWorldLabel,
Constants.RegionWorldData
)
}
onClick={() => {
updateManageRegionData(0, Constants.RegionWorldLabel, getData());
}}>
{
getRegionTree(
Constants.RegionWorldNodeId,
Constants.RegionWorldLabel,
Constants.RegionWorldPosition
)
}
</TreeItem> */}
</TreeView>
</Grid>
)
}
const handleSave = () => {
dispatch(HandleSelectedRegionsAddition(selectedRegionsLocalState));
closeDialog();
}
const handleCancel = () => {
closeDialog();
}
const getDialogFooter = () => {
return (
<Grid className={classes.footer} container justify="space-around">
<Grid item className={classes.footerButtonItems}>
<Button style={{ backgroundColor: Constants.APP_SECONDARY_COLOR }} onClick={handleCancel} className={classes.footerButtons}>
Cancel
</Button>
</Grid>
<Grid item className={classes.footerButtonItems}>
<Button style={{ backgroundColor: Constants.APP_PRIMARY_COLOR }} onClick={handleSave} className={classes.footerButtons}>
Save
</Button>
</Grid>
</Grid>
)
}
return (
<Dialog
classes={{
paper: classes.dialogPaperCustom
}}
open={props.open}
onClose={props.handleClose}>
<DialogTitle className={classes.dialogTitleHeader}>{getDialogHeader()}</DialogTitle>
{
props.open ?
<DialogContent>{getDialogContent()}</DialogContent>
: <React.Fragment />
}
<DialogActions className={classes.dialogActionsFooter}>{getDialogFooter()}</DialogActions>
{/* dummy only during development */}
{/* <Button onClick={handleClearManageRegionData}>clear</Button> */}
</Dialog>
);
}
export default ApiHandlingHOC(ManageRegion);
error i am getting on console


