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>);
}
});
как я могу передать значение onClick событие в React js?
22 ответов:
Легко
используйте функцию стрелки:
return ( <th value={column} onClick={() => this.handleSort(column)}>{column}</th> );это создаст новую функцию, которая вызывает
handleSortс нужными параметрами.Лучше
извлеките его в субкомпонент. Проблема с использованием функции стрелки в вызове рендеринга заключается в том, что она будет создавать новую функцию каждый раз, что в конечном итоге приводит к ненужному повторному рендерингу.
если вы создаете подкомпонент, вы можете передать обработчик и использовать реквизит в качестве аргументы, которые затем будут повторно отображаться только при изменении реквизитов (потому что ссылка на обработчик теперь никогда не меняется):
суб-компонент
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работает, потому что он создает копию функции. Вместо этого мы можем передать выражение функции стрелки, которое просто вызывает нужную нам функцию и сохраняетthis. Вы никогда не должны связыватьrenderметод в React, но если по какой-то причине вы теряетеthisв одном из своих компонентов методы:constructor(props) { super(props); this.myMethod = this.myMethod.bind(this); }
[[h/t to @E. Sundin for связывая это в комментарии]
верхний ответ (анонимные функции или привязка) будет работать, но это не самый эффективный, так как он создает экземпляр обработчика событий для каждого экземпляра генерируется
здесь есть хорошие ответы, и я согласен с @Austin Greco (второй вариант с отдельными компонентами), но я удивлен, что никто не упомянул карринг.
Вы можете создать функцию, которая принимает параметр (ваш параметр) и возвращает другую функцию, которая принимает другой параметр (событие click в этом случае). тогда вы можете делать с ним все, что захотите.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 someArr = ["A", "B", "C", "D"]; class App extends React.Component { 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> {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> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);<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>обратите внимание, что этот подход не решает создание нового экземпляра на каждом рендере.
Мне нравится этот подход по сравнению с другими встроенными обработчиками, поскольку этот более лаконичен и читаем, на мой взгляд.Edit:
Как предлагается в комментариях ниже, вы можете кэшировать / запоминать результат функции.вот наивная реализация:
let memo = {}; const someArr = ["A", "B", "C", "D"]; class App extends React.Component { state = { valueA: "", valueB: "some initial value", valueC: "", valueD: "blah blah" }; handleChange = param => { const handler = e => { const nextValue = e.target.value; this.setState({ ["value" + param]: nextValue }); } if (!memo[param]) { memo[param] = e => handler(e) } return memo[param] }; render() { return ( <div> {someArr.map(obj => { return ( <div key={obj}> <label> {`input ${obj} `} </label> <input type="text" value={this.state["value" + obj]} onChange={this.handleChange(obj)} /> <br /> <br /> </div> ); })} </div> ); } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);<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" />
Это мой подход, не уверен, насколько это плохо, пожалуйста, прокомментируйте
в кликабельном элементе
return ( <th value={column} onClick={that.handleSort} data-column={column}> {column}</th> );а то
handleSort(e){ this.sortOn(e.currentTarget.getAttribute('data-column')); }
этот пример может немного отличаться от вашего. но я могу заверить вас, что это лучшее решение для этой проблемы. я искал в течение нескольких дней для решения, которое не имеет проблемы с производительностью. и наконец-то придумал вот это.
class HtmlComponent 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> ); } }обновление
в случае если вы хотите иметь дело с объектами, которые должны быть параметры. вы можете использовать
JSON.stringify(object)чтобы преобразовать его в строку и добавить в набор данных.return ( <div> <h3 data-param={JSON.stringify({name:'me'})} onClick={this.handleClick}> {this.state.name} </h3> </div> );
еще один вариант, не связанный с
.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функция для дочернего компонента. Дочерний компонент вызывает
class extends React.Component { onClickDiv = (column) => { // do stuff } render() { return <div onClick={() => this.onClickDiv('123')} /> } }
Я написал компонент оболочки, который может быть повторно использован для этой цели, который основывается на принятых здесь ответах. Если все, что вам нужно сделать, это передать строку, а затем просто добавить атрибут данных и прочитать его из e.target.набор данных (как и некоторые другие предложили). По умолчанию моя оболочка будет привязана к любой опоре, которая является функцией и начинается с " on " и автоматически передает опору данных обратно вызывающему после всех других аргументов события. Хотя я не тестировал его на производительность, он будет дайте вам возможность избежать создания класса самостоятельно, и его можно использовать следующим образом:
const DataButton = withData('button')
const DataInput = withData('input');или для компонентов и функций
const DataInput = withData(SomeComponent);или, если вы предпочитаете
const DataButton = withData(<button/>)объявите, что вне вашего контейнера (рядом с вашим импортом)
вот использование в контейнере:
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; }, {}); }
Я добавил код для передачи значения события onclick в метод двумя способами . 1 . используя метод bind 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>); } });
делая альтернативную попытку ответить на вопрос OP, включая e. preventDefault() вызовы:
Rendered link ( ES6)
<a href="#link" onClick={(e) => this.handleSort(e, 'myParam')}>Компонент Функция
handleSort = (e, param) => { e.preventDefault(); console.log('Sorting by: ' + param) }
у меня есть ниже 3 предложения к этому на JSX onClick события -
на самом деле, нам не нужно использовать .функция bind () или Стрелка в нашем коде. Вы можете просто использовать в своем коде.
вы также можете переместить событие onClick из th(или ul) в tr(или li) для повышения производительности. В основном у вас будет n количество "слушателей событий" для вашего элемента N li.
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>// и вы можете получить доступ к
item.idнаonItemClickметод, как показано ниже:onItemClick = (event) => { console.log(e.target.getAttribute("item.id")); }Я согласен с упомянутым выше подходом для создания отдельного компонента React для ListItem и List. Это сделать код выглядит хорошо, однако если у вас есть 1000 li, то 1000 прослушивателей событий будут созданы. Пожалуйста, убедитесь, что вы не должны иметь много прослушивателя событий.
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> ); } }
С помощью функции стрелки:
вы должны установить Этап-2:
npm install babel-preset-stage-2:
class App extends React.Component { constructor(props) { super(props); this.state = { value=0 } } changeValue = (data) => (e) => { alert(data); //10 this.setState({ [value]: data }) } render() { const data = 10; return ( <div> <input type="button" onClick={this.changeValue(data)} /> </div> ); } } export default App;
есть 3 способа справиться с этим :-
свяжите метод в конструкторе как : -
export class HeaderRows extends Component { constructor() { super(); this.handleSort = this.handleSort.bind(this); } }использовать функцию стрелки при создании его как :-
handleSort = () => { // some text here }Третий способ-это :-
<th value={column} onClick={() => that.handleSort} >{column}</th>
Вы можете просто сделать это, если вы используете
ES6.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}> <Input onChange={(event) => this.handItemChange(event, item)} value={item.value}/> </div> )) } </div> ); } }
реализация show total count от объекта путем передачи count в качестве параметра от основных к субкомпонентам, как описано ниже.
вот MainComponent.js
import React, { Component } from "react"; import SubComp from "./subcomponent"; class App extends Component { getTotalCount = (count) => { this.setState({ total: this.state.total + count }) }; state = { total: 0 }; render() { const someData = [ { name: "one", count: 200 }, { name: "two", count: 100 }, { name: "three", count: 50 } ]; return ( <div className="App"> {someData.map((nameAndCount, i) => { return ( <SubComp getTotal={this.getTotalCount} name={nameAndCount.name} count={nameAndCount.count} key={i} /> ); })} <h1>Total Count: {this.state.total}</h1> </div> ); } } export default App;и вот это
SubComp.jsimport React, { Component } from 'react'; export default class SubComp extends Component { calculateTotal = () =>{ this.props.getTotal(this.props.count); } render() { return ( <div> <p onClick={this.calculateTotal}> Name: {this.props.name} || Count: {this.props.count}</p> </div> ) } };попробуйте реализовать выше, и вы получите точный сценарий того, как параметры передачи работают в reactjs на любом методе DOM.
Ниже приведен пример, который передает значение на событие onclick.
я использовал синтаксис es6. помните, что в компоненте класса функция стрелки не привязывается автоматически, поэтому явно привязка в конструкторе.
class HeaderRows extends React.Component { constructor(props) { super(props); this.handleSort = this.handleSort.bind(this); } handleSort(value) { console.log(value); } render() { return( <tr> {this.props.defaultColumns.map( (column, index) => <th value={ column } key={ index } onClick={ () => this.handleSort(event.target.value) }> { column } </th> )} {this.props.externalColumns.map((column, index) => <th value ={ column[0] } key={ index }> {column[0]} </th> )} </tr> ); } }
Я думаю,вам придется привязать метод к экземпляру класса React. Это безопаснее, чтобы использовать конструктор, чтобы связать все методы в реакцию. В вашем случае, когда вы передаете параметр методу, первый параметр используется для привязки контекста "this" метода, поэтому вы не можете получить доступ к значению внутри метода.
1. You just have to use an arrow function in the Onclick event like this: <th value={column} onClick={() => that.handleSort(theValue)} >{column}</th> 2.Then bind this in the constructor method: this.handleSort = this.handleSort.bind(this); 3.And finally get the value in the function: handleSort(theValue){ console.log(theValue); }
есть очень простой способ.
onClick={this.toggleStart('xyz')} . toggleStart= (data) => (e) =>{ console.log('value is'+data); }
думаю,
.bind(this, arg1, arg2, ...)в карте React-это плохой код, потому что он медленный! 10-50 о.bind(this)в одном методе render - очень медленный код.
Я исправляю это так:
метод render<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="https://cdnjs.cloudflare.com/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>
Я бы рекомендовал Карри над привязкой в этом случае. Мол,
return ( <th value={column} onClick={this.handleSort.curry(column)} {column}</th> );
Comments