简体   繁体   English

处理动态生成的按钮上的点击

[英]Handling clicks on dynamically generated buttons

Background背景

Hello, I am very comfortable with C# and VB.net but I'm new to Javascript and currently trying to learn React as well.您好,我对 C# 和 VB.net 非常熟悉,但我是 Javascript 新手,目前也在尝试学习 React。 I'm working on a simple ticket purchase application for learning purposes.我正在开发一个用于学习目的的简单购票应用程序。 I was able to make this application in Angular in a day, but React has been proving to be more difficult.我能够在一天内在 Angular 中制作这个应用程序,但事实证明 React 更加困难。 It is apparent I need to learn JS fundamentals with React, whereas I feel like I didn't need any previous knowledge to pick up Angular.很明显我需要用 React 学习 JS 基础知识,而我觉得我不需要任何以前的知识来学习 Angular。

Issue问题

I spun up a Create React App project and started tinkering with it (I learn best by tinkering).我创建了一个 Create React App 项目并开始修改它(我通过修改学习得最好)。 I'm currently able to generate a button for each Exhibitor (theater chain).我目前能够为每个参展商(剧院链)生成一个按钮。 These buttons are dynamically generated because eventually, I won't know how many exhibitors there are.这些按钮是动态生成的,因为最终我不知道有多少参展商。 After lots of Googling and Stack Overflowing, here's the code I've come up with (please forgive any noob syntax/formatting):经过大量的谷歌搜索和堆栈溢出,这是我想出的代码(请原谅任何菜鸟语法/格式):

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class TicketOrder extends Component {
  constructor () {
    super();
    
    this.handleClick = this.handleClick.bind(this);
    
    // List of Exhibitors
    this.exhibList = {
      TestA     : "abc",
      TestB     : "bcd",
      TestC     : "cde"
    };
  }
  
  render() {
    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Ticketz</h2>
          <p id="userGuide">Hello. Please select an Exhibitor:<br/><br/> </p>
        </div>
        <br/>
        <div>
        {
          Object.keys(this.exhibList).map(function(key) {
            return (<p><button className="button" onClick={key => this.handleClick}>{key}</button></p>);
          })
        }
        </div>
    </div>
    );
  }
  
  handleClick () {
    console.log("Clicked!");
  };
}

export default TicketOrder;

I presume I'm making some fundamental mistakes and I admit I don't fully grasp the React concepts yet, so please go easy on me!我想我犯了一些基本错误,我承认我还没有完全掌握 React 概念,所以请放轻松! The error I'm getting no matter what I try is:无论我尝试什么,我都会遇到的错误是:

"Uncaught TypeError: Cannot read property 'handleClick' of undefined". “未捕获的类型错误:无法读取未定义的属性 'handleClick'”。

I've tried many variations of the above code to no luck.我已经尝试了上述代码的许多变体,但都没有成功。 Would anyone be so kind as to tell me what's wrong with my code/concept?有人会这么好心告诉我我的代码/概念有什么问题吗?

Once this issue is resolved, I still have an underlying issue that I haven't been able to conceptualize yet which is having handleClick know which button was pressed.一旦这个问题得到解决,我仍然有一个潜在的问题,我还无法概念化它是让handleClick知道按下了哪个按钮。 For example, if the user pressed TestA, console.log("abc") .例如,如果用户按下 TestA,则console.log("abc") Eventually, once I understand more about JS and React, I'm guessing I'd be changing the state of the component in handleClick based on the button pressed.最终,一旦我对 JS 和 React 有了更多的了解,我猜我会根据按下的按钮更改handleClick组件的状态。 Any insight on how to accomplish this would be super appreciated.任何有关如何实现这一点的见解将不胜感激。

The "this" you're trying to access is not accessible, because you're in the function scope.您尝试访问的“this”无法访问,因为您在函数范围内。 To avoid it you can use the arrow function syntax.为了避免它,您可以使用箭头函数语法。 Also, the map function is iterating on your array of keys, so you just need to give the handleClick function this key as an argument.此外,map 函数正在迭代您的键数组,因此您只需将此键作为参数提供给 handleClick 函数即可。 Your mapping would like like this:您的映射是这样的:

 {
    Object.keys(this.exhibList).map((item, index) => {
       return (<p><button key={index} className="button" onClick={() => this.handleClick(item)}>{item}</button></p>);
    })
 }

In the arrow function {}, "this" will be the one you want to access (aka TicketOrder).在箭头函数 {} 中,“this”将是您要访问的(又名 TicketOrder)。 Also remember to give a unique key property to any element you map (I put the index in the previous example), this helps React algorithm know which elements changed.还要记住给你映射的任何元素一个唯一的键属性(我在前面的例子中放置了索引),这有助于 React 算法知道哪些元素发生了变化。 Cf here参考这里

Your handleClick would look like this:您的 handleClick 将如下所示:

handleClick = (message) => {
  console.log("Clicked!", message);
};

You can see that a different key is passed as an argument and displayed depending on which button the user clicks on.您可以看到不同的键作为参数传递并根据用户单击的按钮显示。

That's how the whole thing would look for me:这就是整个事情对我的看法:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class TicketOrder extends Component {
  // Drop the constructor you don't need it anymore, especially with Create React App
  exhibList = {
    TestA       : "abc",
    TestB       : "bcd",
    TestC       : "cde",
  }

  handleClick = (key) => {
    console.log("Clicked!", key);
  };

  render() {
    return (
      <div className="App">
          <div className="App-header">
              <img src={logo} className="App-logo" alt="logo" />
              <h2>Ticketz</h2>
              <p id="userGuide">Hello. Please select an Exhibitor:<br/><br/> </p>
          </div>
          <br/>
          <div>
          {
              Object.keys(this.exhibList).map((item, index) => {
                  return (<p><button key={index} className="button" onClick={() => this.handleClick(item)}>{item}</button></p>);
              })
          }
          </div>
     </div>
    );
  }
}

export default TicketOrder;

Change your onClick to this:将您的onClick更改为:

onClick={this.handleClick.bind(this, key)}

and your handleClick to:和您的handleClick以:

 handleClick(ctx) {
   console.log(ctx, "Clicked!");
 }

Change your code like this:像这样改变你的代码:

onClick={() => this.handleClick(key)}

handleClick(key) {
  console.log('Clicked!', key)
}

and if you want the event:如果你想要这个事件:

onClick={event => this.handleClick(event, key)}

handleClick(event, key) {
  console.log('Clicked!', event, key)
}

Hope that helps!希望有帮助! :) :)

Your map function has no idea about this inside, hence Cannot read property 'handleClick' of undefined" . That means you're trying to invoke undefined.handleClick, in that case this is undefined.您的地图函数内部对此一无所知,因此无法读取 undefined" 的属性 'handleClick' 。这意味着您正在尝试调用 undefined.handleClick,在这种情况下, this是未定义的。

You can use fat arrow function () => {} which bind's this to the function out of the box.您可以使用胖箭头函数() => {}this绑定到开箱即用的函数。 To do so change your map function to为此,请将您的地图功能更改为

map((key, index) => {
  return (
     <p key={index}>
     <button
       className="button" 
       onClick={() => this.handleClick(key)}>{key}
     </button>
     </p>);
})

Also note the index argument, each child in iterator should have unique key.还要注意 index 参数,迭代器中的每个孩子都应该有唯一的键。

Then your handleClick:然后你的 handleClick:

handleClick(key){
   console.log('Clicked',key)
}

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

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