React js onClick无法将值传递给方法
我想读取onClick事件值属性。 但是当我点击它,我在控制台上看到这样的东西:
SyntheticMouseEvent {dispatchConfig: Object, dispatchMarker: ".1.1.0.2.0.0:1", nativeEvent: MouseEvent, type: "click", target
我的代码工作正常。 当我运行我可以看到{column}
但不能得到它在onClick事件。
我的代码:
var HeaderRows = React.createClass({ handleSort: function(value) { console.log(value); }, render: function () { var that = this; return( <tr> {this.props.defaultColumns.map(function (column) { return ( <th value={column} onClick={that.handleSort} >{column}</th> ); })} {this.props.externalColumns.map(function (column) { // Multi dimension array - 0 is column name var externalColumnName = column[0]; return ( <th>{externalColumnName}</th> ); })} </tr>); } });
如何将值传递给React js中的onClick
事件?
简单的方法
使用箭头function:
return ( <th value={column} onClick={() => this.handleSort(column)}>{column}</th> );
这将创build一个新的函数,调用handleSort
与正确的参数。
更好的方法
将其解压缩为一个子组件。 在渲染调用中使用箭头函数的问题是每次都会创build一个新函数,最终导致不必要的重新渲染。
如果你创build一个子组件,你可以传递处理函数并使用道具作为参数,然后只有在道具变化时才会重新渲染(因为处理函数引用现在不会改变):
子组件
class TableHeader extends Component { handleClick = () => { this.props.onHeaderClick(this.props.value); } render() { return ( <th onClick={this.handleClick}> {this.props.column} </th> ); } }
主要组件
{this.props.defaultColumns.map((column) => ( <TableHeader value={column} onHeaderClick={this.handleSort} /> ))}
旧的简单方法(ES5)
使用.bind
传递你想要的参数:
return ( <th value={column} onClick={that.handleSort.bind(that, column)}>{column}</th> );
现在,用ES6,我觉得我们可以使用更新的答案。
return ( <th value={column} onClick={()=>this.handleSort(column)} >{column}</th> );
基本上,(对于任何不知道的),因为onClick
期待传递给它的函数, bind
工作,因为它创build一个函数的副本。 相反,我们可以传递一个箭头函数expression式,它只是调用我们想要的函数,并保留this
。 你永远不需要在React中绑定render
方法,但是如果出于某种原因,你在某个组件方法中丢失了this
方法:
constructor(props) { super(props); this.myMethod = this.myMethod.bind(this); }
[[h / t to @ E.Sundin在评论中将其链接 ]
顶级答案(匿名函数或绑定)将会起作用,但它并不是最高性能的,因为它会为map()
函数生成的每个实例创build一个事件处理函数的副本。
这是从ESLint插件反应的最佳方式解释:
项目清单
在渲染时,一个常见的使用情况是在渲染列表的时候,为每个列表项目单独的callback:
const List = props => ( <ul> {props.items.map(item => <li key={item.id} onClick={() => console.log(item.id)}> ... </li> )} </ul> );
而不是这样做,把重复的部分拉成自己的组件:
const List = props => ( <ul> {props.items.map(item => <ListItem key={item.id} item={item} onItemClick={props.onItemClick} // assume this is passed down to List /> )} </ul> ); const ListItem = props => { const _onClick = () => { console.log(props.item.id); } return ( <li onClick={_onClick}> ... </li> ); });
这将加速呈现,因为它避免了在每个呈现器上创build新的函数(通过绑定调用)的需要。
这里有很好的答案,我同意@奥斯汀·格列柯(第二个选项与单独的组件),但我很惊讶没有人提到咖喱 。
你可以做的是创build一个函数,接受一个参数(你的参数),并返回另一个函数接受另一个参数(在这种情况下,点击事件)。 那么你可以随意使用它,你想要什么。
ES5:
handleChange(param) { // param is the argument you passed to the function return function(e) { // e is the event object that returned }; }
ES6:
handleChange = param => e => { // param is the argument you passed to the function // e is the event object that returned };
你会这样使用它:
<input type="text" onChange={this.handleChange(someParam)} />
以下是这种用法的完整示例:
const styles = { fontFamily: "sans-serif", textAlign: "center" }; const someArr = ["A", "B", "C", "D"]; class App extends React.Component { constructor(props) { super(props); this.state = { valueA: "", valueB: "some initial value", valueC: "", valueD: "blah blah" }; } handleChange = param => e => { const nextValue = e.target.value; this.setState({ ["value" + param]: nextValue }); }; render() { return ( <div style={styles}> {someArr.map(obj => { return ( <div> <label> {`input ${obj} `} </label> <input type="text" value={this.state["value" + obj]} onChange={this.handleChange(obj)} /> <br /> <br /> </div> ); })} </div> ); } } ReactDOM.render( < App / > , document.getElementById("root"));
<script src="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>
这是我的方法,不知道它有多糟糕,请评论
在可点击的元素中
return ( <th value={column} onClick={that.handleSort} data-column={column}> {column}</th> );
接着
handleSort(e){ this.sortOn(e.currentTarget.getAttribute('data-column')); }
不涉及.bind
或ES6的另一个选项是使用带有处理程序的子组件来调用具有必要道具的父处理程序。 下面是一个例子(下面是一个工作示例的链接):
var HeaderRows = React.createClass({ handleSort: function(value) { console.log(value); }, render: function () { var that = this; return( <tr> {this.props.defaultColumns.map(function (column) { return ( <TableHeader value={column} onClick={that.handleSort} > {column} </TableHeader> ); })} {this.props.externalColumns.map(function (column) { // Multi dimension array - 0 is column name var externalColumnName = column[0]; return ( <th>{externalColumnName}</th> ); })} </tr>); ) } }); // A child component to pass the props back to the parent handler var TableHeader = React.createClass({ propTypes: { value: React.PropTypes.string, onClick: React.PropTypes.func }, render: function () { return ( <th value={this.props.value} onClick={this._handleClick} {this.props.children} </th> ) }, _handleClick: function () { if (this.props.onClick) { this.props.onClick(this.props.value); } } });
基本的想法是父组件将onClick
函数传递给子组件。 子组件调用onClick
函数,并可以访问传递给它的任何props
(以及event
),从而允许您在父级的onClick
函数中使用任何event
值或其他道具。
这是一个CodePen演示,展示了这个方法的实际应用。
这个例子可能与你的有些不同。 但我可以向你保证,这是你可以解决这个问题的最佳解决scheme。 我已经search了一个没有性能问题的解决scheme。 终于想出了这个。
class HannadComponent extends React.Component { constructor() { super(); this.state={ name:'MrRehman', }; this.handleClick= this.handleClick.bind(this); } handleClick(event) { const { param } = e.target.dataset; console.log(param); //do what you want to do with the parameter } render() { return ( <div> <h3 data-param="value what you wanted to pass" onClick={this.handleClick}> {this.state.name} </h3> </div> ); } }
UPDATE
如果你想处理那些应该是参数的对象。 您可以使用JSON.stringify(object)将其转换为string并添加到数据集。
return ( <div> <h3 data-param={JSON.stringify({name:'me'})} onClick={this.handleClick}> {this.state.name} </h3> </div> );
class extends React.Component { onClickDiv = (column) => { // do stuff } render() { return <div onClick={() => this.onClickDiv('123')} /> } }
我已经添加了两种方法的onclick事件值传递的代码的方法。 1。 使用绑定方法2.使用箭头(=>)方法。 请参阅handlesort1和handlesort方法
var HeaderRows = React.createClass({ getInitialState : function() { return ({ defaultColumns : ["col1","col2","col2","col3","col4","col5" ], externalColumns : ["ecol1","ecol2","ecol2","ecol3","ecol4","ecol5" ], }) }, handleSort: function(column,that) { console.log(column); alert(""+JSON.stringify(column)); }, handleSort1: function(column) { console.log(column); alert(""+JSON.stringify(column)); }, render: function () { var that = this; return( <div> <div>Using bind method</div> {this.state.defaultColumns.map(function (column) { return ( <div value={column} style={{height : '40' }}onClick={that.handleSort.bind(that,column)} >{column}</div> ); })} <div>Using Arrow method</div> {this.state.defaultColumns.map(function (column) { return ( <div value={column} style={{height : 40}} onClick={() => that.handleSort1(column)} >{column}</div> ); })} {this.state.externalColumns.map(function (column) { // Multi dimension array - 0 is column name var externalColumnName = column; return (<div><span>{externalColumnName}</span></div> ); })} </div>); } });
我对JSX onClick事件有以下3条build议 –
-
实际上,我们不需要在代码中使用.bind()或者Arrow函数。 你可以简单地使用你的代码。
-
您也可以将onClick事件从th(或ul)移动到tr(或li)以提高性能。 基本上你将有n个“事件监听器”为你的元素。
So finally code will look like this: <ul onClick={this.onItemClick}> {this.props.items.map(item => <li key={item.id} data-itemid={item.id}> ... </li> )} </ul>
//你可以访问
onItemClick
方法中的onItemClick
,如下所示:onItemClick = (event) => { console.log(e.target.getAttribute("item.id")); }
-
我同意上面提到的为ListItem和List创build单独的React组件的方法。 这使得代码看起来不错,但是如果你有1000个li,那么将会创build1000个事件监听器。 请确保你不应该有太多的事件监听器。
import React from "react"; import ListItem from "./ListItem"; export default class List extends React.Component { /** * This List react component is generic component which take props as list of items and also provide onlick * callback name handleItemClick * @param {String} item - item object passed to caller */ handleItemClick = (item) => { if (this.props.onItemClick) { this.props.onItemClick(item); } } /** * render method will take list of items as a props and include ListItem component * @returns {string} - return the list of items */ render() { return ( <div> {this.props.items.map(item => <ListItem key={item.id} item={item} onItemClick={this.handleItemClick}/> )} </div> ); } } import React from "react"; export default class ListItem extends React.Component { /** * This List react component is generic component which take props as item and also provide onlick * callback name handleItemClick * @param {String} item - item object passed to caller */ handleItemClick = () => { if (this.props.item && this.props.onItemClick) { this.props.onItemClick(this.props.item); } } /** * render method will take item as a props and print in li * @returns {string} - return the list of items */ render() { return ( <li key={this.props.item.id} onClick={this.handleItemClick}>{this.props.item.text}</li> ); } }
我写了一个包装器组件,可以重用这个目的,build立在这里接受的答案。 如果你只需要传递一个string,那么只需添加一个数据属性,并从e.target.dataset中读取(就像其他人所build议的那样)。 默认情况下,我的包装将绑定到任何是一个函数的道具,并以“on”开头,并在所有其他事件参数后自动将数据道具传递给调用者。 虽然我没有testing它的性能,但它会给你机会,避免自己创build类,它可以像这样使用:
const DataButton = withData('button')
const DataInput = withData('input');
或用于组件和function
const DataInput = withData(SomeComponent);
或者如果你愿意
const DataButton = withData(<button/>)
声明在你的容器之外(靠近你的import)
以下是容器中的用法:
import withData from './withData'; const DataInput = withData('input'); export default class Container extends Component { state = { data: [ // ... ] } handleItemChange = (e, data) => { // here the data is available // .... } render () { return ( <div> { this.state.data.map((item, index) => ( <div key={index}> <DataInput data={item} onChange={this.handleItemChange} value={item.value}/> </div> )) } </div> ); } }
这里是包装代码'withData.js:
import React, { Component } from 'react'; const defaultOptions = { events: undefined, } export default (Target, options) => { Target = React.isValidElement(Target) ? Target.type : Target; options = { ...defaultOptions, ...options } class WithData extends Component { constructor(props, context){ super(props, context); this.handlers = getHandlers(options.events, this); } render() { const { data, children, ...props } = this.props; return <Target {...props} {...this.handlers} >{children}</Target>; } static displayName = `withData(${Target.displayName || Target.name || 'Component'})` } return WithData; } function getHandlers(events, thisContext) { if(!events) events = Object.keys(thisContext.props).filter(prop => prop.startsWith('on') && typeof thisContext.props[prop] === 'function') else if (typeof events === 'string') events = [events]; return events.reduce((result, eventType) => { result[eventType] = (...args) => thisContext.props[eventType](...args, thisContext.props.data); return result; }, {}); }
再次尝试回答OP的问题,包括e.preventDefault()调用:
渲染链接( ES6 )
<a href="#link" onClick={(e) => this.handleSort(e, 'myParam')}>
组件function
handleSort = (e, param) => { e.preventDefault(); console.log('Sorting by: ' + param) }
我认为,在React的地图中.bind(this, arg1, arg2, ...)
是不好的代码,因为它很慢! .bind(this)
10-50在单个渲染方法 – 非常慢的代码。
我解决这个问题:
渲染方法
<tbody onClick={this.handleClickRow}>
<tr data-id={listItem.id}>
映射
处理器
var id = $(ev.target).closest('tr').data().id
下面的完整代码:
class MyGrid extends React.Component { // In real, I get this from props setted by connect in Redux state = { list: [ {id:1, name:'Mike'}, {id:2, name:'Bob'}, {id:3, name:'Fred'} ], selectedItemId: 3 } render () { const { list, selectedItemId } = this.state const { handleClickRow } = this return ( <table> <tbody onClick={handleClickRow}> {list.map(listItem =>( <tr key={listItem.id} data-id={listItem.id} className={selectedItemId===listItem.id ? 'selected' : ''}> <td>{listItem.id}</td> <td>{listItem.name}</td> </tr> ))} </tbody> </table> ) } handleClickRow = (ev) => { const target = ev.target // You can use what you want to find TR with ID, I use jQuery const dataset = $(target).closest('tr').data() if (!dataset || !dataset.id) return this.setState({selectedItemId:+dataset.id}) alert(dataset.id) // Have fun! } } ReactDOM.render(<MyGrid/>, document.getElementById("react-dom"))
table { border-collapse: collapse; } .selected td { background-color: rgba(0, 0, 255, 0.3); }
<script src="ajax/libs/react/15.0.2/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="react-dom"></div>
在这种情况下,我会build议咖喱绑定。 喜欢,
return ( <th value={column} onClick={this.handleSort.curry(column)} {column}</th> );