'Reactjs Collapse / expand folder list and displaying data in 2 different ways

I have huge data set with a lot of nested parent/children arrays. My goal is to make these folders and data to look like this: Finished file/folder app. I am currently stuck achieving this:

Task 1: When I click once on a folder in the sidebar it should expand/collapse it's contents. The same click will also make the folder active.

Problem 1: It makes folder active only after few clicks, sometimes at first, it's completely random.

Task 2: When I click on a file or folder in the main area, it activates. When activated, it should also expand the path to the folder in the sidebar tree.

Problem 2: Main area work independently and I can go through folders only inside main and nothing happens in the sidebar (they should be connected to each other) as they are from sidebar to main (I mean when I click on sidebar folder or file, it opens up in main area but when I click on folder or item in the main area - it only changes in main area.

App.js:

import classes from "./App.module.css";
import React, { useState } from "react";
import Context from "./context";
import List from "./List";
import Item from "./Item";

function App(item) {
  const [name, setName] = useState("");
  const [type, setType] = useState("");
  const [children, setChildren] = useState([]);
  const [isExpandedApp, setIsExpandedApp] = useState(false);
  const [activatedApp, setActivatedApp] = useState(false);

  const onClick = (item) => {
    setIsExpandedApp(!isExpandedApp);
    setActivatedApp(!activatedApp);
    setName(item.name);
    setType(item.type);
    item.type === "folder"
      ? setChildren(
          item.children
            .sort(function (a, b) {
              if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
              if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
              return 0;
            })
            .map((x) => <Item key={x.id} item={x} />)
        )
      : setChildren("");
  };

  const providerValue = {
    setChildren,
    onClick,
    isExpandedApp,
    activatedApp,
  };

  return (
    <Context.Provider value={providerValue}>
      <header>
        <h2>Home Assignment</h2>
      </header>

      <section>
        <nav>
          <List />
        </nav>

        {!children && (
          <div className={classes.preview}>
            <h1>Preview</h1>
            <p>Name: {name}</p>
            <p>Type: {type}</p>
          </div>
        )}

        {children && (
          <div className={classes.folderIsActive}>
            <>
              {children.map((x) =>
                x.props.item.type !== "folder" ? (
                  <ul key={x.props.item.id} className={classes.centering}>
                    <ul
                      className={classes.oneLetter}
                      onClick={() => onClick(x.props.item)}
                    >
                      {x.props.item.name.charAt(0)}
                    </ul>
                    {x.props.item.name}
                  </ul>
                ) : (
                  <ul key={x.props.item.id} className={classes.centering}>
                    <ul
                      className={classes.greyFolder}
                      onClick={() => onClick(x.props.item)}
                    ></ul>
                    {x.props.item.name}
                  </ul>
                )
              )}
            </>
          </div>
        )}
      </section>
    </Context.Provider>
  );
}

export default App;

List.js:

import React, { useEffect, useState } from "react";
import Item from "./Item";

export default function List() {
  const [data, setData] = useState([]);

  const getData = () => {
    fetch("data.json", {
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    })
      .then(function (response) {
        return response.json();
      })
      .then(function (myJson) {
        setData(myJson.response);
      });
  };
  useEffect(() => {
    getData();
  }, []);

  return (
    <ul>
      {data
        .sort(function (a, b) {
          if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
          if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
          return 0;
        })
        .map((x) => {
          return <Item key={x.id} item={x} />;
        })}
    </ul>
  );
}

Item.js:

import React, { useState, useContext } from "react";
import classes from "./App.module.css";
import Context from "./context";

export default function Item({ item }) {
  const [isExpandedItem, setIsExpandedItem] = useState(false);
  const [activatedItem, setActivatedItem] = useState(false);

  const { onClick, isExpandedApp, activatedApp } = useContext(Context);

  const setValues = () => {
    setIsExpandedItem(isExpandedApp);
    setActivatedItem(activatedApp);
  };

  const onItemClick = () => {
    onClick(item);
    setValues();
  };

  return (
    <ul>
      <li
        tabIndex="0"
        className={activatedItem ? classes.active : classes.notActive}
        onClick={onItemClick}
      >
        {item.type === "folder" && (
          <i
            className={isExpandedItem ? classes.arrowDown : classes.arrowRight}
          />
        )}
        {item.name}
      </li>

      {isExpandedItem && item.children && (
        <ul>
          {item.children
            .sort(function (a, b) {
              if (a.name.toLowerCase() < b.name.toLowerCase()) return -1;
              if (a.name.toLowerCase() > b.name.toLowerCase()) return 1;
              return 0;
            })
            .map((x) => (
              <Item key={x.id} item={x} />
            ))}
        </ul>
      )}
    </ul>
  );
}

Any tips would be really helpful!



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source