Home > Software engineering >  Adding event listeners using useRef inside a loop
Adding event listeners using useRef inside a loop

Time:10-29

I'm writing a React component that accepts some data as an array and renders them. However, I need to add some event listeners to it. The functions I'll be passing to the event listeners should depend on the data. Basically, the rendered items and their behavior should be completely data-driven.

I wrote this piece of code that adds event listeners, however it seems like it keeps overwriting the event listeners and I always get the function from the last iteration.

const App = () => {
 const data = ['item1', 'item2', 'item3', 'item4', 'item5'];
 const itemRef = React.useRef(data.map((x) => React.createRef(x)));

 React.useEffect(() => {
  for (var i = 0; i < itemRef.current.length; i  ) {

   // need to define a function here that will 
   // accept data[i] as a parameter.
   // That unique function would be added 
   // to event listener on each iteration 

   itemRef.current[i].current.addEventListener('click', () => {
    console.log(`clicked data:${i}`);
   });
  }
 }, []);

 return (
  <div className="App">
   {data.map((x, i) => {
    return (
     <div className='item' ref={itemRef.current[i]} key={data[i]}>
      {x}
     </div>
    );
   })}
  </div>
 );
}
ReactDOM.render(
    <App/>,
    document.getElementById("root")
);
.App {
  text-align: center;
}

.item{
  border: 1px solid black;
  width: 300px;
  margin: 10px auto;
  border-radius: 3px;
  cursor: pointer;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

Ideally, in that () => {console.log(i)} section (inside addEventListener) I want to pass a function that would be unique on each iteration.

Am I taking the right approach with this? Can this code be amended so if I click on item 3 it logs 3 (not 5)?

CodePudding user response:

You needn't resort to refs in this instance, an onClick will work, you can add in a handler, or you can define a function on the fly like below:

const App = () => {
 const data = ['item1', 'item2', 'item3', 'item4', 'item5'];

 return (
  <div className="App">
   {data.map((x, i) => {
    return (
     <div className='item' onClick={()=>{console.log("item data", x)}} key={data[i]}>
      {x}
     </div>
    );
   })}
  </div>
 );
}
ReactDOM.render(
    <App/>,
    document.getElementById("root")
);
.App {
  text-align: center;
}

.item{
  border: 1px solid black;
  width: 300px;
  margin: 10px auto;
  border-radius: 3px;
  cursor: pointer;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

  • Related