对项目不起作用的React OnClick

时间:2017-12-02 07:51:32

标签: javascript reactjs

我有一个react组件,它使用单独的OnClick呈现一个列表项。

为了找出点击了哪个项目,处理程序接受一个参数。处理程序确实被调用 - 但无论单击哪个项目 - 控制台始终记录item3(就像单击item3一样)。我在这里做错了什么?

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>
        );
    }

这一行:

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

似乎是正确的。

3 个答案:

答案 0 :(得分:6)

问题是您没有正确捕获this.items[liName]的值,因为当您到达第三项迭代时,onClick处理程序将始终将this.items[liName]的值设置为第三项。

解决方法是使用闭包来正确捕获值,我编辑了代码并在此链接中创建了一个完整的示例

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

下面用解决方案

编写示例代码
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>
    );
  }
}

有关闭包的更多信息和示例,请查看此link

修改我们可以在@ArchNoob提及的for循环中使用ES6中的let代替var,因为使用let会使{{1}阻止作用域

答案 1 :(得分:2)

发布代码时请注意缩进。没有它,很难理解。你必须利用封闭。每当循环结束时,liName变量被设置为最后一个索引,因为作用域链将使liName值保持为最后一个。该解决方案不是在处理程序和单击处理程序函数之间建立一个新的范围。 这是解决方案:

&#13;
&#13;
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"))
&#13;
<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>
&#13;
&#13;
&#13;

答案 2 :(得分:0)

我将按照推荐的方式进行操作。 首先制作一个单独的列表项组件。 在React 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>
  )
}

现在,项目组件就像

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