30

I have the following component

const list = (props) => {

  const handler = function(){

  };

  var listItems = props.items.map(function(item, index){
    return (
      <li key={index} onClick={ handler }>
        {item.text}
      </li>
    )
  });

  return (
    <div>
      <ul>
        {listItems}
      </ul>
    </div>
  )
}

On Click i'd like to get the index of the li clicked. Using ES6 and without binding how can i do this ?

4

7 Answers 7

42

Use an arrow function.

onClick={() => handler(index)}
Sign up to request clarification or add additional context in comments.

5 Comments

using arrow function is not recommended if using eslint plugin
Then you have to create new items for your components and pass the index prop to it.
This is a soft "bad practice" as everytime the element renders it will create a new function, it could cause double renders in some cases.
Correct way can be found here: stackoverflow.com/questions/29810914/…
Please avoid anonymous functions as much as you can.
17

You can actually get index without using an arrow function. The only thing you need to do is pass the index as an attribute and get that value from the event as e.target.getAttribute("your_attribute_name")

const list = (props) => {

const handler = function(e){
    console.log(e.target.getAttribute("data-index")); //will log the index of the clicked item
};

var listItems = props.items.map(function(item, index){
    return (
    <li key={index} data-index={index} onClick={ handler }>
        {item.text}
    </li>
    )
});

return (
    <div>
        <ul>
            {listItems}
        </ul>
    </div>
    );
}

4 Comments

Why is this a better approach than the accepted answer above?
@Hamund citation from google "If you use arrow functions within render , each call to render will create new function objects. If you then pass these functions to child elements via props , optimizations based on PureComponent or shouldComponentUpdate will fail (as the arrow function props will change on every render)." - So if you are into performance then it's better to pass a ref to a function instead of an arrow function.
@SallyRothroat: this will not work because event.target can be any element inside <li> element.
change to currentTarget
1

you can set the index in the child as data-index and then you get this value in the handler function using event.currentTarget.dataset.index This will prevent the re-rendering that causes when you use arrow function within render.

const handler = (event) => {
    console.log(event.currentTarget.dataset.index);
};

const listItems = props.items.map((item, index) => {
    return (
       <li key={index} data-index={index} onClick={handler}>
          {item.text}
       </li>
    )
});

Comments

1

This also works:

const list = props => {
    const handler = index => () => {

    }

    const listItems = props.items.map((item, index) =>
        <li key={index} onClick={handler(index)}>
            {item.text}
        </li>)

    return <div>
        <ul>{listItems}</ul>
    </div>
}

Comments

1

You have another way of doing it, really easy to pass any variable to the handleClick function.

An that is using a curry function.

const ListComponent= ({listItems}) => {

  const handleClick = (index) => (event) => {
      [...]
  }

  return (
      <ul>
        {listItems.map((item, index) => (
            <li
                key={index}
                onClick={ handler(index) }
            >
                {item.text}
            </li>
         ))}
      </ul>
  )
}

Comments

1

You can just get the index of the currentTarget within its parentElements childs. Like this:

const handleClick = useCallback<React.MouseEventHandler<HTMLElement>>(
    (event) => {
        const currentEl = event.currentTarget;
        const index = [...el.parentElement.children].indexOf(el);
        
        // do stuff with index
        doSomethingWithIndex(index);
    },[]
);

1 Comment

Best answer, because this is a single function without the need for a separate function per element and no need for using data-index={index}
0

You can write an anonymous function.

  <li key={index} onClick={() => handler(index) }>
        {item}
      </li>

Or you can write a child component to render the ListItem and pass index as prop to a child component.

interface ListItemProps { 
  item: string,
  index: number
}
  
const ListItem: React.FC<ListItemProps> = ({ item, index }) => {

  const handler = (index: number) => {
    console.log(" Item clicked: ", index); 
  };
   return   (<li key={index} onClick={() => handler(index) }>
   {item}
 </li>)
    
}


const list = ( ) => {


  const items = ['AAA', 'BBB', 'CCC']
  
  var listItems = items.map( (item:string, index: number) => {
    return <ListItem key={index} item={item} index={index} />
  });

  return (
    <div>
      <ul>
        {listItems}
      </ul>
    </div>
  )
}

First approach is simpler and readable. The second approach is good when the rendering child element is more involved.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.