简体   繁体   English

对项目不起作用的React OnClick

[英]React OnClick for item not working

I have a react component which renders a list item with individual OnClick. 我有一个react组件,它使用单独的OnClick呈现一个列表项。

In order to find out which item was clicked, the handler accepts a parameter. 为了找出单击了哪个项,处理程序接受一个参数。 The handler does get invoked - but no matter which item is clicked - console always logs item3 (as if item3 is clicked). 处理程序确实被调用 - 但无论单击哪个项目 - 控制台始终记录item3(就像单击item3一样)。 What am I doing wrong here? 我在这做错了什么?

class Item {
    constructor(props) {
        super(props);
        this.onItemClickHandler = this.onItemClickHandler.bind(this)
    }

    onItemClickHandler (itemName) {
        console.log("Clicked " + itemName)
    }

    render() {
        this.items = ["item1", "item2", "item3"]
        var lis = []
        for (var liName in this.items) {
            var liName2 = this.items[liName]
            console.log("Adding " + this.items[liName])
            lis.push(<li className="item-ListItem" key={this.items[liName]} onClick={() => this.onItemClickHandler(this.items[liName])}><span><a href="#">{this.items[liName]}</a></span></li>)
        }

        return (
          <div className="item">
            <label className="item-Header"><u>items</u></label>
            <ul className="item-List"> 
            {lis}
            </ul>

          </div>
        );
    }

This line: 这一行:

onClick={() => this.onItemClickHandler(this.items[liName])}>

appears to be correct. 似乎是正确的。

The issue is that you are not capturing the value of this.items[liName] correctly because by the time you reach the third item iteration the onClick handler will always have the value of this.items[liName] set to the third item. 问题是您没有正确捕获this.items[liName]的值,因为当您到达第三项迭代时, onClick处理程序将始终将this.items[liName]的值设置为第三项。

The solution for that is using closure to capture the value correctly, i edited your code and created a fully working example in this link 解决方案是使用闭包来正确捕获值,我编辑了您的代码并在此链接中创建了一个完整的示例

https://codesandbox.io/s/3xrp6k9yvp https://codesandbox.io/s/3xrp6k9yvp

Also the example code is written below with the solution 下面用解决方案编写示例代码

class App extends Component {
  constructor(props) {
    super(props);
    this.onItemClickHandler = this.onItemClickHandler.bind(this);
  }

  onItemClickHandler(itemName) {
    console.log("Clicked " + itemName);
  }

  render() {
    this.items = ["item1", "item2", "item3"];
    var lis = [];
    for (var liName in this.items) {
      var liName2 = this.items[liName];
      console.log("Adding " + this.items[liName]);

      //the clickHandler function here is the solution we created a function that get executed immediately each iteration and return a new function that has the correct value of `this.items[liName]` saved
      var clickHandler = (item => {
        return event => {
          this.onItemClickHandler(item);
        };
      })(this.items[liName]);

      lis.push(
        <li
          className="item-ListItem"
          key={this.items[liName]}
          onClick={clickHandler} // here we use the clickHandler function directly
        >
          <span>
            <a href="#">{this.items[liName]}</a>
          </span>
        </li>
      );
    }

    return (
      <div className="item">
        <label className="item-Header">
          <u>items</u>
        </label>
        <ul className="item-List">{lis}</ul>
      </div>
    );
  }
}

For more info and examples about closures check this link 有关闭包的更多信息和示例,请检查此链接


Edit We can use let in ES6 instead of var in our for loop as mentioned by @ArchNoob because using let will make the liName block scoped 编辑我们可以在@ArchNoob中提到的for循环中使用let in ES6而不是var ,因为使用let将使liName块作用域

Please take care of indentation while posting the code. 发布代码时请注意缩进。 Its very difficult to understand without that. 没有它,很难理解。 You have to make use of closure. 你必须利用封闭。 whenever the loop gets over liName variable gets set to last index as scope chain will keep the liName value to last one. 每当循环结束时,liName变量被设置为最后一个索引,因为作用域链将使liName值保持为最后一个。 The solution is not make a new scope between handler and click handler function where it is callled. 该解决方案不是在处理程序和单击处理程序函数之间建立一个新的范围。 Here is the solution: 这是解决方案:

 class Test extends React.Component { constructor(props) { super(props) this.onItemClickHandler = this.onItemClickHandler.bind(this) } onItemClickHandler(itemName) { debugger console.log("Clicked " + itemName) } render() { this.items = ["item1", "item2", "item3"] var lis = [] for (var liName in this.items) { var liName2 = this.items[liName] console.log("Adding " + this.items[liName]) debugger lis.push( <li className = "item-ListItem" key = { this.items[liName] } onClick = { ((item) => { return () => this.onItemClickHandler(item) })(this.items[liName]) } > <span> <a href = "#"> {this.items[liName]} </a> </span> </li> ) } return ( <div> <div className="item"> <label className="item-Header"> <u>items</u> </label> <ul className="item-List" > {lis} </ul> </div> </div> ) } } ReactDOM.render(<Test />, document.getElementById("root")) 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="root"></div> 

I will do it the recommended way. 我会以推荐的方式做到这一点。 First make a separate component of list-item. 首先创建list-item的单独组件。 To render list in react Js 在反应Js中呈现列表

//Handler
clickHandler=(id)=>{
    console.log(`Clicked on Item with Id ${id}`);
}

//render method
render(){
  let items = null;
  if(this.state.isAnyItem){
   <div className="items-list">
      {
        this.state.items.map((item)=>{
          <Item key={item.id} item={item} click={(item.id)=>this.clickHandler(item.id)}/>
         })
       }
   </div>
  }

return (
   <div>
     {/*Some JSX here*/}
        {items}
    </div>
  )
}

And Now the Item Component like 现在项目组件就像

<div onClick={props.click}>
    {/*Some JSX Here*/}
    <h3>{item.name}</h3>
</div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM