我无法从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>
);
}
}
答案 0 :(得分:28)
首先,您要将已评估的函数调用传递给onClick
属性。
单独查看以下代码:
function foo(name) { return 'hello ' + name; }
foo('bob');
您希望foo('bob')
的输出为“hello bob”。
如果我把它传递到onClick
道具上,那就完全相同了。例如:
<button onClick={foo('bob')}
在这种情况下,我只是将一个字符串传递给按钮的onClick
道具。 onClick
(以及其他事件道具)期望提供函数(或ref)。我将进一步说明这一点。
其次,我认为您有正确的意图,尝试使用const self = this
和bind
的组合来维护您的函数的正确范围。使用ES6 / 2015中的匿名函数可以更轻松地完成此操作,这些函数始终保持声明它们的声明范围。
然后我可以将您的代码更新为以下内容:
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
}
但这仍然不是最佳的。当使用这样的匿名语法(例如<foo onClick={() => { console.log('clicked') }
)时,我们在每个渲染上创建一个新函数。如果您使用纯组件(即仅在提供新的prop实例时应重新渲染的组件 - 这个检查通过浅层比较执行),这可能是一个问题。始终尝试尽早创建您的功能,例如在构造函数中或通过类属性,如果您使用babel阶段1,那么您总是传入相同的引用。
但是,对于您的示例,您需要将值“传递”到每个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
}
这是一个更好的方法。另请注意,我为每个生成的行添加了key
道具。这是另一种反应最佳实践,因为反应将在其差异算法中使用唯一的密钥标识符。
我强烈推荐以下内容:
答案 1 :(得分:1)
除了@ctrlplusb answer之外,我还建议另一种在点击事件中传递对象的方法:
//歌曲组件
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>
);
}
}
//行组件
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>
);
}
答案 2 :(得分:0)
使用变量绑定来避免在渲染器上调用函数:self.fetchDetails.bind(song)