0

So I'm losing my mind over here trying to essentially just select the sibling element of things that are mapped.

I basically map over an object from an API, grab some stuff from it, and display some text and an ul with some li in it. I ultimately want to click on h2 and have it alter the ul below it. In this case, I want the ul item below it to have a display of none until clicked. I also tried returning the JSX of the ul in the onClick of the h2 tag, but it didn't add any elements to the page.

This wouldn't be so bad if I knew what anything was going to be, and it was a set object, but being an API, I could have 1 or 1000 ul to work with that all need the same functionality. I was reading to do it by state, but I don't know how many items there are going to be, so how would I set state on an undefined number of items? Also, wouldn't having 100 states for this be crazy if that's what my API returned??

    return myData.map((obj) => (
  <div style={{ maxWidth: "500px", marginLeft: "25%" }} key={obj.name}>
    <img
      src={obj.image}
      width="500px"
    ></img>
    <h1>{obj.name}</h1>
    <h2 id={'ingredients'+Math.Random} onClick={(e) => {console.log(e)}}>Ingredients</h2>
    <ul id={'UL'+Math.random()}>
      {obj.ingredients.map((index) => (
        <ListItem  key={"ingredients" + Math.random()} value={index} />
      ))}
    </ul>

Side note - this code is ultimately for my practice only, so ignore bad naming etc...

Ken White
  • 123,280
  • 14
  • 225
  • 444
Sporadic
  • 140
  • 6

1 Answers1

1

You can achieve this by only using one state. Assuming your obj.name is unique since you are using it as a key value. You can use this name to determine wheter or not to display your ul, if the id is in the array then display the ul, if not do not display.

const YourComponent = () => {
  // state to track all the ids of all the expanded ul's
  const [expandedIds, setExpandedIds] = useState([]);

  const toggleVisibility = (idToToggle) => {
    // set new state
    setExpandedIds((prevIds) => {
      // if the id to toggle is in the array, if not add it
      if (!expandedIds.includes(idToToggle)) return [...prevIds, idToToggle];
      // else remove the id
      return prevIds.filter((id) => id !== idToToggle);
    });
  };

  return myData.map((obj) => (
    <div style={{ maxWidth: "500px", marginLeft: "25%" }} key={obj.name}>
      <img src={obj.image} width="500px"></img>
      <h1>{obj.name}</h1>
      <h2 onClick={() => toggleVisibility(obj.name)}>Ingredients</h2>
      {expandedIds.includes(obj.name) && (
        <ul id={"UL" + Math.random()}>
          {obj.ingredients.map((index) => (
            <ListItem key={"ingredients" + Math.random()} value={index} />
          ))}
        </ul>
      )}
    </div>
  ));
};

I hope this helps you with your project.

RubenSmn
  • 4,091
  • 2
  • 5
  • 24
  • thank you very much. It does work perfectly, however I'm confused at one point of the code which I need clarified if I ever want to do something similar in another scenario. {expandedIds.includes(obj.name) && (
      {obj.ingredients.map((index) => ( ))}
    )} What exactly is happening here? With no if statement etc I am not really sure how this is working and what the second half of the && is checking?
    – Sporadic Oct 22 '22 at 23:48
  • Okay so upon further investigation, I modified the && to a ternary operator ( cause it helped me understand what was happening using something I know). I then researched && more and it appears you wrote it that way as kind of a "trick" to not have to worry about saying what to do if false, because it returns the first falsy value. So it either just returns falsy, or if the first thing is true it automatically returns the 2nd half. I always thought && was just for if statements etc... and I didnt know you could just drop it anywhere and get a result. Am I correct about this? – Sporadic Oct 23 '22 at 00:14
  • Yes you are, I found [this](https://stackoverflow.com/questions/61166026/understanding-the-use-of-in-a-react-component) SO answer about the `&&` which explains it quite well. – RubenSmn Oct 23 '22 at 05:51