0

Apologies if this is poorly written (first time posting here so feedback on how to better write posts welcome!)

I am using react map to iterate through lists of data.

                          {level1Folders.level2Folders.map(
                        (level2Folders, index) => {
                          return (
                            <li
                              id={level2Folders.folderLevel2Name}
                              key={level2Folders.folderLevel2Name + index}
                            >
                              <div
                                className="menu-item-folder-level-2"
                                onClick={() =>
                                  hideMenuItem(
                                    level2Folders.folderLevel2Name
                                  )
                                }
                              >
                                <FaIcons.FaCaretRight />
                                {level2Folders.folderLevel2Name}
                              </div>

                              <ul
                                className="manuals d-none"
                                id={level2Folders.folderLevel2Name}
                              >
                                {level2Folders.manuals.map(
                                  (manual, index) => {
                                    return (
                                      <li key={manual + index} id={manual}>
                                        <div
                                          onClick={() =>
                                            handleExplorerItemClick(manual)
                                          }
                                          className="menu-item-manual"
                                        >
                                          {manual}
                                        </div>
                                      </li>
                                    );
                                  }
                                )}

I have a method hideMenuItem(menuItemId) which will hide items based on their id's, so the idea is to set the id = to the name of the item, so when the parent item is clicked the child elements will be hidden.

  function hideMenuItem(menuItemId) {
console.log(menuItemId);
let x = document.getElementById(menuItemId);
if (x.classList.contains('d-block')) {
  x.classList.add('d-none');
  x.classList.remove('d-block');
} else {
  x.classList.add('d-block');
  x.classList.remove('d-none');

}
 }

I have 5 uses of this - level2Folders.folderLevel2Name, the only one that won't work is when trying to enter this as a parameter in hideMenuItem(menuItemId), the value here is returned as the index of the item.

aspen
  • 1

1 Answers1

0

The point here is you want to toggle the item's child by using the element's classlist. It might be better if you change your approach and use react ways to achieve your goals. One of them is conditional styling where you can read here for the details.

For your case, let me show you one of many approach which using state in show and hide elements. Try absorp the concept and implement it at yours.

First, the data should have name which is the name of folder, showSubFolders which is property that will keep the boolean value, and subFolders that contains the sub folders details.

const folderData = [
  {
    name: "My Documents",
    showSubFolders: false,
    subFolders: [
      {
        name: "My Music",
        icon: faMusic
      },
      {
        name: "My Images",
        icon: faImage
      }
    ]
  },
  ...
}

Then, set a folders state that will keep the folder's data in our component:

export default function OurComponent() {
  const [folders, setFolders] = useState(folderData);
  ...
}

Since we use <FontAwesomeIcon icon={...} />, so we can render the main folder icon by using conditional icon selection which depends on folder's showSubFolders property value:

return (
  ...
  <ul>
     {folders.map((folder, index) => {
        return (
           <li>
              <FontAwesomeIcon
                 icon={folder.showSubFolders ? faFolderOpen : faFolder}
              />
           </li>
           ...
        )
      })}
   </ul>
   ...
)
     

The next is toggle subFolders section by creating a toggleFolder method that use useCallback hooks and depends on folders state. We negate the value of showSubFolders property if the folder name equal to the argument name supplied.

const toggleFolder = useCallback(
   (name) => {
      const toggledFolders = folders.map((folder) => {
        if (folder.name === name) {
          folder.showSubFolders = !folder.showSubFolders;
        }
        return folder;
      });
      setFolders(toggledFolders);
   },
   [folders]
);

And we can call the toggleFolder method from our list item as follow:

   return (
      ...
      {folders.map((folder, index) => {
          return (
            <li key={index} style={{ cursor: "pointer" }}>
               <span onClick={() => toggleFolder(folder.name)}>
                  <FontAwesomeIcon
                     icon={folder.showSubFolders ? faFolderOpen : faFolder}
                  />
                  {folder.name}
               </span>
               ...
            </li>
            ...
          )
      })}
      ...
   )
    

And when the folders state change, the component will re-render and we can use conditional render technique {folder.showSubFolders && ( ... ) here:

   <li>
     ...
       {folder.showSubFolders && (
         <ul>
           {folder.subFolders.map((subFolder, index) => {
             return (
               <li key={index}>
                 <FontAwesomeIcon icon={subFolder.icon} />
                 {subFolder.name}
               </li>
             );
           })}
         </ul>
      )}
   </li>

Of course, this is not the only way to achieve your goal, but it is more React Way in doing so.

And lastly, this is the final code:

Edit compassionate-water-08xdv0

yohanes
  • 2,365
  • 1
  • 15
  • 24