Search code examples
javascriptreactjssortingrecursionjsx

Trouble creating Nested Dom Node structure


I am trying to create nested dom node structure (something like folder tree) using ul and li tags for react component. This nested structure is based on "path" key from objects of input array provided in code below

Below is the code that I am using to create dom node

let pathArray = []
let nested = [];
let rootNode = [];

let data = [
    {
        "id": "bbc57df8b48fa010b34d3d586bcdb63e12fcc230",
        "name": "Hooks",
        "type": "tree",
        "path": "Hooks",
        "mode": "040000"
    },
    {
        "id": "bbc57df8b48fa010b34d3d586bcdb63e12fcc230",
        "name": "ab1",
        "type": "tree",
        "path": "Hooks/ab1",
        "mode": "040000"
    },
    {
        "id": "bbc57df8b48fa010b34d3d586bcdb63e12fcc230",
        "name": "ab2",
        "type": "tree",
        "path": "Hooks/ab2",
        "mode": "040000"
    },
    {
        "id": "bbc57df8b48fa010b34d3d586bcdb63e12fcc230",
        "name": "bb2",
        "type": "tree",
        "path": "Hooks/ab1/bb2",
        "mode": "040000"
    },
    {
        "id": "6d20bb276234ea4b97ee8e47a8efc5ae945cdc65",
        "name": "Routing",
        "type": "tree",
        "path": "Routing",
        "mode": "040000"
    },
    {
        "id": "23602d99d0680243f32f07410b5c227f0d0c0331",
        "name": "L1.png",
        "type": "blob",
        "path": "Hooks/L1.png",
        "mode": "100644"
    },
    {
        "id": "e89d96e5e23a7f0e7cb240b28dace2259328b3ee",
        "name": "ReadMe.md",
        "type": "blob",
        "path": "Hooks/ReadMe.md",
        "mode": "100644"
    },
    {
        "id": "e89d96e5e23a7f0e7cb240b28dace2259328b3ee",
        "name": "ab1.md",
        "type": "blob",
        "path": "Hooks/ab1/ab1.md",
        "mode": "100644"
    },
    {
        "id": "e89d96e5e23a7f0e7cb240b28dace2259328b3ee",
        "name": "ab2.md",
        "type": "blob",
        "path": "Hooks/ab2/ab2.md",
        "mode": "100644"
    },
    {
        "id": "e89d96e5e23a7f0e7cb240b28dace2259328b3ee",
        "name": "bb2.md",
        "type": "blob",
        "path": "Hooks/ab1/bb2/bb2.md",
        "mode": "100644"
    },
    {
        "id": "23602d99d0680243f32f07410b5c227f0d0c0331",
        "name": "L1.png",
        "type": "blob",
        "path": "L1.png",
        "mode": "100644"
    },
    {
        "id": "b014be6ae81b88953520d2e43c94fa403a6384f8",
        "name": "ReadMe.md",
        "type": "blob",
        "path": "Routing/ReadMe.md",
        "mode": "100644"
    },
    {
        "id": "77c1b823a94a7f3441c785ef27a072ce6db6b0e4",
        "name": "file.md",
        "type": "blob",
        "path": "file.md",
        "mode": "100644"
    },
    {
        "id": "def1f0ba9051393b4128d0ecb8d35cb4780c936c",
        "name": "test.md",
        "type": "blob",
        "path": "test.md",
        "mode": "100644"
    }
];


const createNestedDOM = (path) => {

    console.log("path : " + path)
    let array = data.filter(child => child.path.startsWith(path + '/'))


    console.log("array : " + JSON.stringify(array))
    console.log("current pathArray : " + JSON.stringify(pathArray))

    // Loop through array elements
    array.forEach((item) => {
        console.log(" Current item : " + JSON.stringify(item))

        if (!pathArray.includes(item.path)) {
            pathArray.push(item.path)

            if (item.type === "tree") {

                console.log("pushing UL path  : " + item.path)
                return (<ul key={item.path} onClick={toggleDisplay} className={`${styles.boardPageList} my-2 py-2 px-1`}>
                    <div className='d-flex justify-content-between'><span>{item.name}</span> <ChevronDown className='align-self-center' size={16} /></div>
                    {createNestedDOM(item.path)}
                </ul>);
            }
            else {

                console.log("pushing LI path : " + item.path)
                return (<li key={item.path} value={item.name} onClick={(e) => { openBoard(e, item.name) }} className={`${styles.boardPageListItem}`}>
                    {item.name}
                </li>)

            }
        }
    });
};



const createDirTree = () => {
    rootNode.forEach((item) => {

        let node = undefined;
        console.log("item from parent : " + JSON.stringify(item))
        pathArray.push(item.path)
        if (item.type === "tree") {
            node = (<ul key={item.path} onClick={toggleDisplay} className={`${styles.boardPageList} my-2 py-2 px-1`}>
                <div className='d-flex justify-content-between'><span>{item.name}</span> <ChevronDown className='align-self-center' size={16} /></div>
                {createNestedDOM(item.path)}
            </ul>)
        }
        else {
            node = (<li key={item.path} value={item.name} onClick={(e) => { openBoard(e, item.name) }} className={`${styles.boardPageListItem} my-2 py-2`}>
                {item.name}
            </li>)
        }

        console.log(node)
        nested.push(node)

    })
}


for (let index = 0; index < data.length; index++) {
    let obj = data[index]
    let arrPath = obj.path.split("/")
    if (arrPath.length == 1) {
        rootNode.push(obj)
    }
}

createDirTree()

The expected result is it should return ul element with nested ul and li elements for nested paths based on input. But upon execution , there was no nested element under ul element of "Hooks"

current output for hooks

As you can see in above output snippet, the childrens after 1st div child of ul element for "Hooks" are undefined where I was expecting nested elements.

Can anyone guide me on what I am missing or getting wrong over here? Apologies for lengthy code. Just beginner at this things. TIA !!


Solution

  • The issue you're facing lies in the createNestedDOM function. It's currently not returning any JSX elements explicitly, which is why you're getting undefined as children of the ul element.

    Since Array.prototype.forEach doesn't return anything, the return values from within the forEach callback aren't propagating up.

    Instead of using forEach, you might want to use map, which returns a new array with the results of calling a provided function on every element in the calling array.

    Try using Map if that helps.

      const createNestedDOM = (path) => {
        let array = data.filter(child => child.path.startsWith(path + '/'))
        // Mapping array elements to JSX elements
        let elements = array.map((item) => {
            if (!pathArray.includes(item.path)) {
                pathArray.push(item.path)
    
                if (item.type === "tree") {
                    return (
                        <ul key={item.path} onClick={toggleDisplay} className={`${styles.boardPageList} my-2 py-2 px-1`}>
                            <div className='d-flex justify-content-between'><span>{item.name}</span> <ChevronDown className='align-self-center' size={16} /></div>
                            {createNestedDOM(item.path)}
                        </ul>
                    );
                } else {
                    return (
                        <li key={item.path} value={item.name} onClick={(e) => { openBoard(e, item.name) }} className={`${styles.boardPageListItem}`}>
                            {item.name}
                        </li>
                    );
                }
            }
        });
    
        return elements; // Return the JSX elements
         };