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?

Ответ 1

Простой способ

Используйте функцию стрелки:

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

Ответ 2

Здесь есть хорошие ответы, и я согласен с @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>

Ответ 3

В настоящее время, с 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);
}

Ответ 4

[[h/t to @E.Sundin для ссылка на это в комментарии]

Верхний ответ (анонимные функции или привязка) будет работать, но он не самый результативный, поскольку он создает копию обработчика события для каждого экземпляра, сгенерированного функцией map().

Это объяснение оптимального способа сделать это из ESLint-plugin-react:

Списки элементов

Общим примером использования bind в рендеринге является рендеринг списка, отдельный обратный вызов для элемента списка:

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

});

Это ускорит рендеринг, поскольку это позволит избежать необходимости создавать новые функции (через вызовы связывания) на каждом рендере.

Ответ 5

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

В интерактивном элементе

return (
    <th value={column} onClick={that.handleSort} data-column={column}>   {column}</th>
);

а затем

handleSort(e){
    this.sortOn(e.currentTarget.getAttribute('data-column'));
}

Ответ 6

этот пример может немного отличаться от вашего. но я могу заверить вас, что это лучшее решение для этой проблемы. Я искал несколько дней решение, которое не имеет проблемы с производительностью. и, наконец, придумал это.

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

UPDATE

если вы хотите иметь дело с объектами, которые должны быть параметрами. Вы можете использовать JSON.stringify(object), чтобы преобразовать его в строку и добавить в набор данных.

return (
   <div>
     <h3 data-param={JSON.stringify({name:'me'})} onClick={this.handleClick}>
        {this.state.name}
     </h3>
   </div>
);

Ответ 7

class extends React.Component {
    onClickDiv = (column) => {
        // do stuff
    }
    render() {
        return <div onClick={() => this.onClickDiv('123')} />
    }
}

Ответ 8

Еще одна опция, не включающая .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 и может получить доступ к любому переданному ему propsevent), позволяя использовать любое значение event или другие реквизиты в родительской функции onClick.

Здесь приведена демо-версия CodePen, показывающая этот метод в действии.

Ответ 9

Я понимаю, что это довольно поздно для вечеринки, но я думаю, что гораздо более простое решение может удовлетворить многие варианты использования:

    handleEdit(event) {
        let value = event.target.value;
    }

    ...

    <button
        value={post.id}
        onClick={this.handleEdit} >Edit</button>

Я полагаю, вы также можете использовать data-.

Простой, семантический.

Ответ 10

Я написал компонент оболочки, который можно использовать повторно для этой цели, основываясь на принятых ответах здесь. Если все, что вам нужно сделать, это передать строку, однако, просто добавьте атрибут data и прочитайте его с e.target.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;
    }, {});
}

Ответ 11

Я добавил код для значения события 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>);
    }
});

Ответ 12

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

Вот 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>
    )
  }
};

Попробуйте реализовать выше, и вы получите точный сценарий того, как параметры передачи работают вactjs для любого метода DOM.

Ответ 13

У меня ниже 3 предложений на JSX onClick Events -

  • На самом деле нам не нужно использовать функцию .bind() или Arrow в нашем коде. Вы можете просто использовать в своем коде.

  • Вы также можете переместить событие 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 пользователей, тогда будет создано 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>
            );
        }
    }
    

Ответ 14

Выполнение альтернативной попытки ответить на вопрос OP, включая вызовы e.preventDefault():

Ссылка (ES6)

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

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

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

Ответ 15

Используя функцию стрелки:

Вы должны установить этап 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; 

Ответ 16

Вы можете просто сделать это, если вы используете 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>
     );
   }
 }

Ответ 17

Ниже приведен пример, который передает значение на событие 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>
         );
    }
}

Ответ 18

Я предполагаю, что вам придется привязать метод к экземпляру класса Reacts. Безопаснее использовать конструктор для связывания всех методов в React. В вашем случае, когда вы передаете параметр методу, первый параметр используется для привязки этого контекста метода, поэтому вы не можете получить доступ к значению внутри метода.

Ответ 19

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

Ответ 20

Это очень простой способ.

 onClick={this.toggleStart('xyz')} . 
  toggleStart= (data) => (e) =>{
     console.log('value is'+data);  
 }

Ответ 21

class TableHeader extends Component {
  handleClick = (parameter,event) => {
console.log(parameter)
console.log(event)

  }

  render() {
    return (
      <button type="button" 
onClick={this.handleClick.bind(this,"dataOne")}>Send</button>
    );
  }
}

Ответ 22

Есть 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>
    

Ответ 23

Я нашел решение передачи param в качестве атрибута тега вполне разумным.

Однако у него есть ограничения:

  • Не работает должным образом, когда элемент списка имеет другие теги (таким образом, event.target может отличаться от запланированного)
  • Param может быть только строкой. Требуется сериализация и десериализация.

Вот почему я придумал эту библиотеку: response-event-param

Он:

  • Решает проблему детей путем поиска необходимых атрибутов у родителей при необходимости
  • Автоматически сериализует и десериализует параметр
  • Инкапсулирует логику установки и получения. Не нужно связываться с именами параметров

Пример использования:

import { setEventParam, getEventParam } from "react-event-param";

class List extends Component {
  onItemClick = e => {
    const index = getEventParam(e.target);
    // Do something with index
  };

  render() {
    return (
      <ul>
        {this.props.items.map((itemText, index) => (
          <li
            key={index}
            {...setEventParam(index)}
            onClick={this.onItemClick}
          >
            {{ itemText }}
          </li>
        ))}
      </ul>
    );
  }
}

export default List;

Ответ 24

Вы можете использовать свой код следующим образом:

<th value={column} onClick={(e) => that.handleSort(e, column)} >{column}</th>

Здесь e для объекта события, если вы хотите использовать методы события, такие как preventDefault() в своей функции дескриптора или хотите получить целевое значение или имя, например, e.target.name.

Ответ 25

Функция стрелки решит проблему.

Ответ 26

Есть несколько способов передать параметр в обработчиках событий, некоторые из них следующие.

Вы можете использовать функцию стрелки, чтобы обернуть вокруг обработчик события и передать параметры:

<button onClick={() => this.handleClick(id)} />

приведенный выше пример эквивалентен вызову .bind, или вы можете явно вызвать bind.

<button onClick={this.handleClick.bind(this, id)} />

Помимо этих двух подходов, вы также можете передавать аргументы в функцию, которая определена как функция карри.

handleClick = (id) => () => {
    console.log("Hello, your ticket number is", id)
};

<button onClick={this.handleClick(id)} />

Ответ 27

Я думаю, .bind(this, arg1, arg2, ...) в карте React - это плохой код, потому что он медленный! 10-50 из .bind(this) в одном методе рендеринга - очень медленный код.
Я исправлю это так:
Метод рендеринга
<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>

Ответ 28

Я бы порекомендовал curry over bind в этом случае. Например,

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