Maximum call stack size exceeded React Tree View Component Material-UI

Viewed 229

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

enter image description here

enter image description here

enter image description here

0 Answers
Related