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

    };
}

ЕС6:

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" />

Это мой подход, не уверен, насколько это плохо, пожалуйста, прокомментируйте

в элементе clickable

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.цель.dataset (как и некоторые другие). По умолчанию моя обертка будет привязываться к любой опоре, которая является функцией, и начинается с " 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. использование метода arrow (=>). см. методы 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 ():

вынесено ссылке (ЕС6)

<a href="#link" onClick={(e) => this.handleSort(e, 'myParam')}>

Компонент Функция

handleSort = (e, param) => {
  e.preventDefault();
  console.log('Sorting by: ' + param)
}

у меня есть ниже 3 предложения к этому на JSX onclick Events -

  1. на самом деле, нам не нужно использовать .функция bind () или Стрелка в нашем коде. Вы можете просто использовать в своем коде.

  2. вы также можете переместить событие 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"));
    }
    
  3. Я согласен с упомянутым выше подходом для создания отдельного компонента React для ListItem и List. Это делает код выглядит хорошо, однако, если у вас есть 1000 ли, то 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 установить 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 способа справиться с этим :-

  1. привязать метод в конструкторе как : -

    export class HeaderRows extends Component {
       constructor() {
           super();
           this.handleSort = this.handleSort.bind(this);
       }
    }
    
  2. используйте функцию стрелки при ее создании как : -

    handleSort = () => {
        // some text here
    }
    
  3. Третий способ-это :-

    <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>
     );
   }
 }

реализация показать общее количество от объекта путем передачи count в качестве параметра от main к sub компонентам, как описано ниже.

вот 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.js

import 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. Безопаснее использовать конструктор для привязки всех методов в 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) in single render method-очень медленный код.
Я исправляю это так:
метод 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>

в этом случае я бы рекомендовал Карри над bind. Как,

return (   
    <th value={column} onClick={this.handleSort.curry(column)}  {column}</th> 
);