Javascript React - 在表格行上触发点击事件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37771316/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 20:42:25  来源:igfitidea点击:

React - Triggering click event on table row

javascriptreactjs

提问by Ranjith Nair

I am having trouble to invoke click event from a table row in React. Below is my code. I have a separate function to populate the rows alone, but it seems i am messing up the binding information to that.

我无法从 React 中的表行调用 click 事件。下面是我的代码。我有一个单独的函数来单独填充行,但似乎我把绑定信息弄乱了。

import React, {Component, PropTypes} from 'react';
import Header from './Header';

export default class SongData extends Component {
 constructor(props, context) {
    super(props, context);
 }

isEmpty(obj) {
    return Object.keys(obj).length === 0;
}

fetchDetails(song) {
    console.log(song);
}

renderResultRows(data) {
    var self = this;
    return data.map(function(song) {
        return (
            <tr onClick={self.fetchDetails(song)}>
                <td data-title="Song">{song.S_SONG}</td>
                <td data-title="Movie">{song.S_MOVIE}</td>
                <td data-title="Year">{song.S_YEAR}</td>
            </tr>
        );
    }.bind(this));
}

render() {
    return (
        <div id="no-more-tables">
            <table className="table table-hover table-bordered table-striped">
                <thead>
                    <tr>
                        <th>Song</th>
                        <th>Movie</th>
                        <th>Year</th>
                    </tr>
                </thead>
                <tbody>
                    {!this.isEmpty(this.props.searchResult)
                        ? this.renderResultRows(this.props.searchResult)
                        : ''}
                </tbody>
            </table>
        </div>
    );
}

}

}

回答by ctrlplusb

Firstly, you are passing an evaluated function call to your onClickproperty.

首先,您将评估的函数调用传递给您的onClick属性。

See the following code in isolation:

单独看下面的代码:

 function foo(name) { return 'hello ' + name; }

 foo('bob');

You expect the output of foo('bob')to be "hello bob".

您希望 的输出为foo('bob')“hello bob”。

It's the exact same if I passed it into on onClickprop. For example:

如果我将它传递给onClickprop,则完全相同。例如:

 <button onClick={foo('bob')}

In this case I will just be passing a string to the onClickprop for the button. The onClick(and other event props) expect a function (or ref) to be provided. I'll give an example of this a bit further down.

在这种情况下,我只会将一个字符串传递给onClick按钮的道具。的onClick(和其他事件的道具)希望被提供的功能(或ref)。我将在下面举一个例子。

Secondly I see you have the right intention in trying to maintain the correct scope for your functions using a combination of const self = thisand bind. There is an easier way to do so using anonymous functions from ES6/2015, which always maintain the scope of where they were declared.

其次我看你有正确的意向试图维持使用的组合为你的函数正确的范围const self = thisbind。使用 ES6/2015 中的匿名函数有一种更简单的方法,它始终保持声明它们的范围。

I could then update your code to the following:

然后我可以将您的代码更新为以下内容:

renderResultRows(data) {
    return data.map((song) => {  // anon func maintains scope!
        // Pass in a function to our onClick, and make it anon
        // to maintain scope.  The function body can be anything
        // which will be executed on click only.  Our song value
        // is maintained via a closure so it works.
        return (
            <tr onClick={() => this.fetchDetails(song)}>
                <td data-title="Song">{song.S_SONG}</td>
                <td data-title="Movie">{song.S_MOVIE}</td>
                <td data-title="Year">{song.S_YEAR}</td>
            </tr>
        );
    });  // no need to bind with anon function
}

But this is still not optimal. When using the anonymous syntax like this (e.g. <foo onClick={() => { console.log('clicked') }) we are creating a new function on every render. This could be an issue if you are using pure components (i.e. components that should only re-render when new prop instances are provided - this check being performed via a shallow compare). Always try to create your functions early, e.g. in the constructor or via class properties if you are using babel stage 1, that way you always pass in the same reference.

但这仍然不是最优的。当使用像这样的匿名语法(例如<foo onClick={() => { console.log('clicked') })时,我们在每次渲染时创建一个新函数。如果您使用纯组件(即仅在提供新 prop 实例时才应重新渲染的组件 - 此检查通过浅比较执行),这可能是一个问题。始终尝试尽早创建您的函数,例如在构造函数中或通过类属性(如果您使用 babel stage 1),这样您将始终传递相同的引用。

For your example however you require a value to be "passed into" each of the onClickhandlers. For this you could use the following approach:

但是,对于您的示例,您需要将一个值“传递给”每个onClick处理程序。为此,您可以使用以下方法:

fetchSongDetails = () => {
  const song = e.target.getAttribute('data-item');
  console.log('We need to get the details for ', song);
}

renderResultRows(data) {
    return data.map((song, index) => {  // anon func maintains scope!
        // Pass in a function to our onClick, and make it anon
        // to maintain scope.  The function body can be anything
        // which will be executed on click only.  Our song value
        // is maintained via a closure so it works.
        return (
            <tr key={index} data-item={song} onClick={this.fetchSongDetails}>
                <td data-title="Song">{song.S_SONG}</td>
                <td data-title="Movie">{song.S_MOVIE}</td>
                <td data-title="Year">{song.S_YEAR}</td>
            </tr>
        );
    });  // no need to bind with anon function
}

That's a much better approach to follow. Also note that I added a keyprop to each row being generated. This is another react best practice as react will use the unique key identifiers in it's diff'ing algorithm.

这是一个更好的方法。另请注意,我key为正在生成的每一行添加了一个道具。这是另一个反应最佳实践,因为反应将在其差异算法中使用唯一的密钥标识符。



I highly recommend the following reading:

我强烈推荐以下阅读:

回答by Idan Dagan

In addition to @ctrlplusb answer, I would like to suggest another approach to pass an object in the click event:

除了@ctrlplusb answer 之外,我还想建议另一种在 click 事件中传递对象的方法:

// Song Component

// 歌曲组件

import React from 'react';
import RowComponent from './RowComponent';

export default class SongData extends React.PureComponent {

  fetchDetails(songObj) {
    // whatever
  }

  renderResultRows(data) {
    return data.map(songObj => (
      <RowComponent
        key={songObj.id}
        data={songObj}
        onClick={this.fetchDetails}
      />;
  }

  render() {

    const { data } = this.props;

    return (
      <div>
        <table>
         <thead>
         </thead>
         <tbody>
          {
            this.renderResultRows(data)
          }
         </tbody>
        </table>
      </div>
    );
  }
}

// Row Component

// 行组件

import React from 'react';

export function RowComponent(props) {
  const { data, onClick } = props;

  function handleClick() {
    onClick(data);
  }

  return (
    <tr onClick={handleClick}>
      <td data-title="Song">{data.S_SONG}</td>
      <td data-title="Movie">{data.S_MOVIE}</td>
      <td data-title="Year">{data.S_YEAR}</td>
    </tr>
  );
}