【问题标题】:React OnClick for item not working对项目不工作的反应 OnClick
【发布时间】:2017-12-02 07:51:31
【问题描述】:

我有一个反应组件,它使用单个 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])}>

似乎是正确的。

【问题讨论】:

标签: javascript reactjs


【解决方案1】:

问题是您没有正确捕获 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


编辑我们可以在 ES6 中使用 let 而不是 @ArchNoob 提到的 for 循环中的 var,因为使用 let 将使 liName 块作用域

【讨论】:

  • 谢谢 - 个人更喜欢下面 stack26 的代码 - 因为它不使用 var clickHandler - 但我假设你这样做是为了清楚起见 - 如果我只是指 stack26 的代码本身 - 我想我会感到困惑。
  • 不客气......很高兴它有帮助:)......是的,最好在不使用变量的情况下使用立即调用的函数......但正如你所说,在当前情况下它会令人困惑直接将其全部放在处理程序中
【解决方案2】:

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

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>

【讨论】:

    【解决方案3】:

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

    现在的 Item 组件就像

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-07-11
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      • 2017-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多