user992731
user992731

Reputation: 3520

InsertBefore firstChild on event handler using React and Js

I have a working prototype where I cant seem to figure out when I click on a particular button in my nav, then pass that index to another component. It doesn't update the index when I query using "document.querySelector". My goal is to just move the content to the front child position/firstChild depending on what button you clicked. So if you clicked the first button then the last, my last content would be first and so and so on...I've tried a timeout and still no luck.

JS:

.....

const Context = createContext(false);

const dataArr = [
  {
    id: "113062",
    name: "Blue",
    region: "US",
    sort: "1"
  },
  {
    id: "115102",
    name: "Red",
    region: "DE",
    sort: "2"
  },
  {
    id: "70884",
    name: "Green",
    region: "US",
    sort: "3"
  },
  {
    id: "114683",
    name: "Yellow",
    region: "US",
    sort: "4"
  },
  {
    id: "112878",
    name: "Pale",
    region: "US",
    sort: "5"
  },
  {
    id: "114682",
    name: "Orange",
    region: "US",
    sort: "6"
  },
  {
    id: "120093",
    name: "Black",
    region: "CH",
    sort: "8"
  },
  {
    id: "120594",
    name: "White",
    region: "CH",
    sort: "9"
  }
];

const useStyles = makeStyles((theme) => ({
  root: {
    display: "flex",
    "& > *": {
      margin: theme.spacing(1)
    }
  },
  grey: {
    border: "4px solid white"
  },
  orange: {
    color: theme.palette.getContrastText(deepOrange[500]),
    backgroundColor: deepOrange[500],
    border: "4px solid black"
  },
  info: {
    margin: "10px"
  },
  wrapper: {
    display: "flex"
  },
  contentWrapper: {
    display: "flex",
    flexDirection: "column"
  },
  elWrapper: {
    opacity: 0,
    "&.active": {
      opacity: 1
    }
  }
}));

const ToggleItem = ({ id, description }) => {
  const { handleChange, selected, classes } = useContext(Context);
  const isSelected = selected.includes(description);

  const handleClick = (idx) => {
    console.log("idx====", idx);
    handleChange(description, idx);
  };

  return (
    <>
      <Avatar
        className={isSelected ? classes.orange : classes.grey}
        onClick={() => handleClick(id)}
      >
        <span style={{ fontSize: ".75rem" }}>{description}</span>
      </Avatar>
    </>
  );
};

const ToggleContainer = ({ selected, list }) => {
  const isSelected = list.filter((element) => selected.includes(element));
  return (
    <>
      {dataArr.map((item, idx) => (
        <div
          key={idx}
          className="carouselWrapper"
          style={{ display: isSelected.includes(item.name) ? "block" : "none" }}
        >
          {item.name}
        </div>
      ))}
    </>
  );
};

const ToggleWrapper = () => {
  const data = [];
  dataArr.map((el) => data.push(el.name));
  const classes = useStyles();
  const [selected, setSelected] = useState([]);
  const [viewAll, setViewAll] = useState(true);

  const handleChange = (val, idx) => {
    setViewAll(false);
    setSelected((selected) => {
      if (selected.length === 1 && selected.includes(val)) {
        return handleViewAll();
      }
      if (selected.includes(val)) {
        return selected.filter((v) => v !== val);
      } else {
        return [val, ...selected];
      }
    });

    setTimeout(() => {
      const someParentObject = document.getElementById("contentWrapper");
      const someChildObject = document.querySelectorAll(".carouselWrapper")[
        idx
      ];

      // eslint-disable-next-line no-unused-expressions
      selected.length > 0 &&
        someParentObject.insertBefore(
          someChildObject,
          someParentObject.firstChild
        );

      console.log(
        "someParentObject.children[0]==",
        someParentObject.firstChild
      );
      console.log("someChildObject", someChildObject);
    }, 2000);
  };

  const handleViewAll = () => {
    setViewAll(true);
    setSelected([]);
  };

  return (
    <Context.Provider
      value={{
        viewAll,
        handleChange,
        handleViewAll,
        selected,
        classes
      }}
    >
      <div className={classes.wrapper}>
        <Avatar
          className={viewAll ? classes.orange : null}
          onClick={handleViewAll}
        >
          <span style={{ fontSize: "0.75rem", textAlign: "center" }}>
            View All
          </span>
        </Avatar>
        {dataArr.map((d, id) => {
          return (
            <div key={id}>
              <ToggleItem id={id} description={d.name} />
            </div>
          );
        })}
      </div>
      <div id="contentWrapper" className={classes.contentWrapper}>
        <ToggleContainer selected={viewAll ? data : selected} list={data} />
      </div>
    </Context.Provider>
  );
};

export default function App() {
  return <ToggleWrapper />;
}

Upvotes: 0

Views: 391

Answers (1)

pilchard
pilchard

Reputation: 12952

Here is a very quick example of one option. It stores the data in a Map which allows for efficient retrieval (this could be moved to context to avoid passing it to multiple child components) and tracks selected by id in a separate state array. It's then just a matter of mapping the selected array to get your ordered list. If selected is empty render the full values() of the Map.

const { useState, useEffect } = React;

function App({ data }) {
  const [selected, setSelected] = useState([]);
  const [itemMap, setItemMap] = useState(new Map());

  useEffect(() => {
    setItemMap(new Map(data.map(({ id, ...rest }) => [id, { id, ...rest }])));
  }, [data]);

  const selectHandler = (id) => {
    setSelected(selected => {
      if (id === undefined) return [];

      return selected.includes(id) 
        ? selected.filter(_id => _id !== id) 
        : [id, ...selected];
    });
  }

  return (
    <div>
      <Header itemMap={itemMap} selected={selected} selectHandler={selectHandler} />
      <Selected itemMap={itemMap} selected={selected} />
    </div>
  );
}

function Header({ itemMap, selected, selectHandler }) {

  return (
    <div className={"header-container"}>
      <div className={!selected.length && 'selected'} onClick={() => selectHandler()}>All</div>
      {[...itemMap.values()].map(({ id, name }) => (
        <div key={id} className={selected.includes(id) && 'selected'} onClick={() => selectHandler(id)}>
          {name}
        </div>
      ))}
    </div>
  );
}

function Selected({ itemMap, selected }) {

  return (
    <div className={"selected-container"}>
      {selected.length
        ? selected.map((id) => (
          <div key={id} >
            {itemMap.get(id).name}
          </div>
        ))
        : [...itemMap.values()].map(({ id, name }) => (
          <div key={id} >
            {name}
          </div>
        ))
      }
    </div>
  );
}

const dataArr = [{ id: "113062", name: "Blue", region: "US", sort: "1" }, { id: "115102", name: "Red", region: "DE", sort: "2" }, { id: "70884", name: "Green", region: "US", sort: "3" }, { id: "114683", name: "Yellow", region: "US", sort: "4" }, { id: "112878", name: "Pale", region: "US", sort: "5" }, { id: "114682", name: "Orange", region: "US", sort: "6" }, { id: "120093", name: "Black", region: "CH", sort: "8" }, { id: "120594", name: "White", region: "CH", sort: "9" }];

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<App data={dataArr} />);
.header-container {  width: 100vw;  display: flex;  justify-content: flex-start;}
.header-container div {  width: 40px;  height: 40px;  display: flex;  overflow: hidden;  position: relative;  margin: 4px;  font-size:.75rem;  align-items: center;  user-select: none;  border-radius: 50%;  justify-content: center;  color: #fafafa;  background-color: #bdbdbd;}
.header-container div.selected {  background-color: tomato;}
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

<div id='root'></div>

Upvotes: 1

Related Questions