Home > Enterprise >  Need help creating a folder tree component in React with the given json structure
Need help creating a folder tree component in React with the given json structure

Time:06-23

I am trying to build a folder tree component where initially the component will only render the names of items at the first layer (item1, item2, item3), then when an specific item is clicked, it would show me everything listed on the next layer for that item(i.e. click on item1 would show item1.1 and item1.2, click on item1.1 will show item1.1.1). This should be done in a loop until it reaches the final layer with the just the attributes (attr1, attr2, attr3).

Note: I have simplified the item names for demo purposes, but in reality they don't follow a specific pattern/naming system.

import React, {useState, useEffect} from 'react';


const Testing = () => {
    const [expand, setExpand] = useState(false);
    
    const data = {
        "item1": {
            "item1.1": {
                "item1.1.1": {
                    "item1.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                }
            },
            "item1.2": {
                "item1.2.1": {
                    "item1.2.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                }
            }
        },
        "item2": {
            "item2.1": {
                "item2.1.1": {
                    "item2.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                },
                "item2.1.2": {
                    "item2.1.2.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    },
                    "item2.1.2.2": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                }
            }
        },
        "item3": {
            "item3.1": {
                "item3.1.1": {
                    "item3.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                },
                "item3.1.2": {
                    "attr1": [],
                    "attr2": "",
                    "attr3": []
                }
            }
        }

    }

    function parse(data) {
        if (typeof data === 'object') {
            return (
                <li>
                    <ul>
                        {Object.entries(data).map(([key, value]) => (
                            <>
                                <li>
                                    {key}: {typeof value === 'string' ? value : ''}
                                </li>
                                {parse(value)}
                            </>
                        ))}
                    </ul>
                </li>
            );
        }
        if (typeof data === 'object') return (<li>{data}</li>);
        return null;
    }
    
    return (
        <div>
            <div className="App">
                {Object.entries(data).map(([key, value]) => {
                    return (
                        <ul>
                            <span onClick={() => setExpand(!expand)}>{key}</span>
                            <br />
                            <div style={{ display: expand ? "block" : "none", paddingLeft: 15 }}>
                                {parse(value)}
                            </div>
                        </ul>
                    );
                })}
            </div>
        </div>
    )
}

export default Testing;

Here a picture of the output I'm getting right now with the code above: current output; it shows item1, item2, item3 in a list; but when I click on any of them, it will just render everything else, I'm trying to find a way so that it behaves similar to a file directory tree as shown in the demo here: https://codesandbox.io/s/folder-structuring-t6oj4

CodePudding user response:

The issue is that in the demo they use a recursive component that has its own expand state managed with a hook. So every Folder will have a distinct value for the expand variable that is inside their scope.

On the other hand, your Testing component manages a single expand state and the whole rendering uses that state for every "folder" that's the reason why when you click a folder it toggles the state for the whole component, what you should do is refactor your component so that it also manages its own state in each Folder, something like this which is in the demo:

import React, { useState } from "react";

function Folder ({ name, file }) {
  const [expand, setExpand] = useState(false);
  if (typeof file !== "object") return <span>{file}</span>;
  return (
    <div>
      <span onClick={() => setExpand(!expand)}>{name}</span>
      <br/>
      <div style={{ display: expand ? "block" : "none", paddingLeft: 15 }} >
        {
          Object.entries(file).map(([key, value]) => (
            <Folder key={key} name={key} file={value} />
          ))
        }
      </div>
    </div>
  );
}

And then just call it in your component:

import React from 'react';


const Testing = () => {
    const data = {
        "item1": {
            "item1.1": {
                "item1.1.1": {
                    "item1.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                }
            },
            "item1.2": {
                "item1.2.1": {
                    "item1.2.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                }
            }
        },
        "item2": {
            "item2.1": {
                "item2.1.1": {
                    "item2.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                },
                "item2.1.2": {
                    "item2.1.2.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    },
                    "item2.1.2.2": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                }
            }
        },
        "item3": {
            "item3.1": {
                "item3.1.1": {
                    "item3.1.1.1": {
                        "attr1": [],
                        "attr2": "",
                        "attr3": []
                    }
                },
                "item3.1.2": {
                    "attr1": [],
                    "attr2": "",
                    "attr3": []
                }
            }
        }

    }
    
    return (
        <div>
            <div className="App">
                <Folder name="/root" file={data} />
            </div>
        </div>
    )
}

Edit: Refactored the code so that only the selected folder expands on click

  • Related