2

I want to toggle only the last span element that says "text to toggle for {user.id}", I only want to toggle this element for that specific li element, not all the li elements. Currently how I have set up, it will toggle that span element for all the li elements due to how state works. I was thinking of passing an index via the map function and referencing the index to toggle the element I want, but how can I do that? Because the state is set for all the li elements, is there a better way to do this rather than using state?

I know with vanilla JS I could just use getElementById and set the style of it to hide or block. But with react I wasn't sure the best way to handle this, because I was taught we want to avoid IDs when we can with react. Then as far as using useRef() goes I wasn't sure that would really work here because I would need to create a refs for each element and I don't know that I can do that dynamically or if that would be the best practice for that matter?

Thank you!

import React, { useState } from 'react';

const Users = [
  { id: 1, name: 'Andy', age: 32 },
  { id: 2, name: 'Bob', age: 30 },
  { id: 3, name: 'Tom Hulk', age: 40 },
  { id: 4, name: 'Tom Hank', age: 50 },
  { id: 5, name: 'Audra', age: 30 },
  { id: 6, name: 'Anna', age: 68 },
  { id: 7, name: 'Tom', age: 34 },
  { id: 8, name: 'Tom Riddle', age: 28 },
  { id: 9, name: 'Bolo', age: 23 },
];

function Test() {

  const [toggle, setToggle] = useState('none');

  function toggleFunction () {
      toggle === 'none' ? setToggle('block') : setToggle('none')
  }

  return (
    <div className="container">

      <div className="user-list">
        {
          Users.map((user) => (
            <li key={user.id} className="user">          
              <button onClick={toggleFunction}>Button</button>
              <span className="user-id">{user.id}</span>
              <span className="user-name">{user.name}</span>
              <span className="user-age">{user.age} year old</span>
              <span style={{display: toggle}}>text to toggle for {user.id}</span>
            </li>
          ))
         }
      </div>
    </div>
  );
}

export default Test;
Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
Jay
  • 79
  • 1
  • 9

1 Answers1

7

You want to keep track of toggle state for each element.

export default function App() {
  const [toggle, setToggle] = React.useState({});

  function toggleFunction(id) {
    setToggle({
      ...toggle,
      [id]: !toggle[id],
    });
  }

  return (
    <div className="container">
      <div className="user-list">
        {Users.map((user) => (
          <li key={user.id} className="user">
            <button onClick={() => toggleFunction(user.id)}>Button</button>
            <span className="user-id">{user.id}</span>
            <span className="user-name">{user.name}</span>
            <span className="user-age">{user.age} year old</span>
            <span style={{ display: toggle[user.id] ? 'block' : 'none' }}>
              text to toggle for {user.id}
            </span>
          </li>
        ))}
      </div>
    </div>
  );
}

toggle keeps track of which items were toggled or not, by associating id of an item with its toggle state (we use bracket notation for storing this info), e.g.

{8: true, 9: true}

Then in render we read toggle value of an item using its id to check if it was toggled or not using toggle[user.id]

Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
  • Thanks! So I want to make sure I understand what is happening here With the toggleFunction, we set the toggle state, copying over the whole array but then what is this doing? ``` [id: !toggle[id] ``` is this adding the id if it doesn't exist? Lastly I see we pass the id via the onclick, but for the display toggle[user.id] how does that work, I see we use a ternary operator, but how is the state working there like how does it know if its true or false? – Jay May 08 '22 at 19:59
  • @Jay check the updated answer. – Giorgi Moniava May 08 '22 at 20:29