Reagir js onClick não pode passar valor ao método

Eu quero ler as propriedades de valor do evento onClick. Mas quando eu clico nele, vejo algo assim no console:

SyntheticMouseEvent {dispatchConfig: Object, dispatchMarker: ".1.1.0.2.0.0:1", nativeEvent: MouseEvent, type: "click", target 

Meu código está funcionando corretamente. Quando eu corro, consigo ver {column} mas não consigo entrar no evento onClick.

Meu Código:

 var HeaderRows = React.createClass({ handleSort: function(value) { console.log(value); }, render: function () { var that = this; return(  {this.props.defaultColumns.map(function (column) { return ( {column} ); })} {this.props.externalColumns.map(function (column) { // Multi dimension array - 0 is column name var externalColumnName = column[0]; return ( {externalColumnName} ); })} ); } }); 

Como posso passar um valor para o evento onClick no React js?

Jeito fácil

Use uma function de seta:

 return (  this.handleSort(column)}>{column} ); 

Isso criará uma nova function que chama handleSort com os parâmetros corretos.

Melhor maneira

Extraia em um subcomponente. O problema em usar uma function de seta na chamada de renderização é que ela criará uma nova function toda vez, o que acaba causando re-renderizações desnecessárias.

Se você criar um subcomponente, poderá passar o manipulador e usar props como argumentos, que só serão renderizados novamente quando os props forem alterados (porque a referência do manipulador agora nunca muda):

Subcomponente

 class TableHeader extends Component { handleClick = () => { this.props.onHeaderClick(this.props.value); } render() { return (  {this.props.column}  ); } } 

Componente principal

 {this.props.defaultColumns.map((column) => (  ))} 

Caminho Fácil Antigo (ES5)

Use .bind para passar o parâmetro desejado:

 return ( {column} ); 

Hoje em dia, com o ES6, sinto que poderíamos usar uma resposta atualizada.

 return ( this.handleSort(column)} >{column} ); 

Basicamente, (para qualquer um que não saiba) já que o onClick está esperando uma function passada para ele, o bind funciona porque cria uma cópia de uma function. Em vez disso, podemos passar uma expressão de function de seta que simplesmente invoca a function que queremos e preserva this . Você nunca deve ligar o método de render no React, mas se por algum motivo você estiver perdendo this em um dos seus methods de componentes:

 constructor(props) { super(props); this.myMethod = this.myMethod.bind(this); } 

[[h / t para @ E.Sundin por vincular isso em um comentário]

A resposta principal (funções anônimas ou vinculação) funcionará, mas não é a que tem melhor desempenho, pois cria uma cópia do manipulador de events para cada instância gerada pela function map() .

Esta é uma explicação da maneira ideal de fazer isso a partir do ESLint-plugin-react :

Listas de Itens

Um caso de uso comum de bind in render é ao renderizar uma lista, para ter um retorno de chamada separado por item de lista:

 const List = props => ( 
    {props.items.map(item =>
  • console.log(item.id)}> ...
  • )}
);

Em vez de fazer desta forma, puxe a seção repetida para o seu próprio componente:

 const List = props => ( 
    {props.items.map(item => )}
); const ListItem = props => { const _onClick = () => { console.log(props.item.id); } return (
  • ...
  • ); });

    Isso acelerará a renderização, pois evita a necessidade de criar novas funções (por meio de chamadas de bind) em cada renderização.

    Há boas respostas aqui, e eu concordo com @Austin Greco (a segunda opção com componentes separados), mas estou surpreso que ninguém mencionou o currying .
    O que você pode fazer é criar uma function que aceite um parâmetro (seu parâmetro) e retorne outra function que aceite outro parâmetro (o evento click nesse caso). então você é livre para fazer com o que você quiser.

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

    E você vai usar assim:

      

    Aqui está um exemplo completo de tal uso:

     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 ( 
    {someArr.map(obj => { return (


    ); })}
    ); } } ReactDOM.render( < App / > , document.getElementById("root"));
       

    Esta é a minha abordagem, não tenho certeza o quão ruim é, por favor, comente

    No elemento clicável

     return (  {column} ); 

    e depois

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

    Este exemplo pode ser um pouco diferente do seu. mas posso assegurar-lhe que esta é a melhor solução que você pode ter para este problema. Eu pesquisei por dias para uma solução que não tem problema de desempenho. e finalmente surgiu com este.

     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 ( 

    {this.state.name}

    ); } }

    ATUALIZAR

    caso você queira lidar com objects que deveriam ser os parâmetros. você pode usar JSON.stringify (object) para converter para string e adicionar ao dataset.

     return ( 

    {this.state.name}

    );

    Mais uma opção que não envolve .bind ou ES6 é usar um componente filho com um manipulador para chamar o manipulador pai com os acessórios necessários. Aqui está um exemplo (e um link para o exemplo de trabalho está abaixo):

     var HeaderRows = React.createClass({ handleSort: function(value) { console.log(value); }, render: function () { var that = this; return(  {this.props.defaultColumns.map(function (column) { return (  {column}  ); })} {this.props.externalColumns.map(function (column) { // Multi dimension array - 0 is column name var externalColumnName = column[0]; return ( {externalColumnName} ); })} ); ) } }); // 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 (  ) }, _handleClick: function () { if (this.props.onClick) { this.props.onClick(this.props.value); } } }); 

    A idéia básica é que o componente pai passe a function onClick para um componente filho. O componente filho chama a function onClick e pode acessar qualquer props transmitido a ela (e ao event ), permitindo que você use qualquer valor de event ou outros objects dentro da function onClick do pai.

    Aqui está uma demonstração do CodePen mostrando este método em ação.

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

    Eu escrevi um componente wrapper que pode ser reutilizado para esse propósito, baseado nas respostas aceitas aqui. Se tudo que você precisa fazer é passar uma string no entanto, basta adicionar um atributo de dados e lê-lo a partir de e.target.dataset (como alguns outros sugeriram). Por padrão, meu wrapper se ligará a qualquer prop que seja uma function e iniciará com ‘on’ e automaticamente repassará o prop da dados para o responsável pela chamada após todos os outros argumentos do evento. Embora eu não tenha testado para o desempenho, ele lhe dará a oportunidade de evitar a criação da class, e pode ser usado assim:

    const DataButton = withData('button')

    const DataInput = withData('input');

    ou para componentes e funções

    const DataInput = withData(SomeComponent);

    ou se você preferir

    const DataButton = withData()

    declarar que fora do seu contêiner (perto de suas importações)

    Aqui está o uso em um contêiner:

     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 ( 
    { this.state.data.map((item, index) => (
    )) }
    ); } }

    Aqui está o código do wrapper ‘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 {children}; } 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; }, {}); } 

    Eu adicionei código para o valor do evento onclick passar para o método de duas maneiras. 1 usando o método bind 2. usando o método seta (=>). veja os methods handlesort1 e 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( 
    Using bind method
    {this.state.defaultColumns.map(function (column) { return (
    {column}
    ); })}
    Using Arrow method
    {this.state.defaultColumns.map(function (column) { return (
    that.handleSort1(column)} >{column}
    ); })} {this.state.externalColumns.map(function (column) { // Multi dimension array - 0 is column name var externalColumnName = column; return (
    {externalColumnName}
    ); })}
    ); } });

    Tentativa alternativa de responder à pergunta do OP, incluindo as chamadas e.preventDefault ():

    Link renderizado ( ES6 )

      this.handleSort(e, 'myParam')}> 

    Função Componente

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

    Eu tenho abaixo de 3 sugestões para isso em Eventos JSX onClick –

    1. Na verdade, não precisamos usar a function .bind () ou Arrow em nosso código. Você pode usar simples no seu código.

    2. Você também pode mover o evento onClick de th (ou ul) para tr (ou li) para melhorar o desempenho. Basicamente, você terá n número de “Ouvintes de Eventos” para o seu elemento n li.

       So finally code will look like this: 
        {this.props.items.map(item =>
      • ...
      • )}

      // E você pode acessar o item.id no método onItemClick como mostrado abaixo:

       onItemClick = (event) => { console.log(e.target.getAttribute("item.id")); } 
    3. Concordo com a menção de abordagem acima para criar um React Component separado para ListItem e List. Este código de make parece bom, no entanto, se você tiver 1000 de li, em seguida, 1000 ouvintes de evento serão criados. Por favor, certifique-se de que você não deve ter muito ouvinte de evento.

       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 ( 
      {this.props.items.map(item => )}
      ); } } 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 (
    4. {this.props.item.text}
    5. ); } }

    Usando a function de seta:

    Você deve instalar o estágio 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 ( 
    ); } } export default App;

    Existem 3 maneiras de lidar com isso:

    1. Vincule o método no construtor como: –

       export class HeaderRows extends Component { constructor() { super(); this.handleSort = this.handleSort.bind(this); } } 
    2. Use a function de seta ao criá-lo como: –

       handleSort = () => { // some text here } 
    3. A terceira maneira é esta:

        that.handleSort} >{column} 

    Você pode simplesmente fazer isso se estiver usando o ES6 .

     export default class Container extends Component { state = { data: [ // ... ] } handleItemChange = (e, data) => { // here the data is available // .... } render () { return ( 
    { this.state.data.map((item, index) => (
    this.handItemChange(event, item)} value={item.value}/>
    )) }
    ); } }

    A implementação mostra contagem total de um object passando count como um parâmetro de main para subcomponentes, conforme descrito abaixo.

    Aqui está 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 ( 
    {someData.map((nameAndCount, i) => { return ( ); })}

    Total Count: {this.state.total}

    ); } } export default App;

    E aqui está SubComp.js

     import React, { Component } from 'react'; export default class SubComp extends Component { calculateTotal = () =>{ this.props.getTotal(this.props.count); } render() { return ( 

    Name: {this.props.name} || Count: {this.props.count}

    ) } };

    Tente implementar acima e você terá um cenário exato de como os parâmetros de passagem funcionam em reactjs em qualquer método DOM.

    Abaixo está o exemplo que passa valor no evento onClick.

    Eu usei a syntax es6. lembre-se na function de seta de componente de class não se liga automaticamente, então explicitamente vinculativo no construtor.

     class HeaderRows extends React.Component { constructor(props) { super(props); this.handleSort = this.handleSort.bind(this); } handleSort(value) { console.log(value); } render() { return(  {this.props.defaultColumns.map( (column, index) =>  this.handleSort(event.target.value) }> { column }  )} {this.props.externalColumns.map((column, index) =>  {column[0]}  )}  ); } } 

    Eu acho que você terá que vincular o método à instância de class do React. É mais seguro usar um construtor para vincular todos os methods no React. No seu caso, quando você passa o parâmetro para o método, o primeiro parâmetro é usado para vincular o contexto ‘this’ do método, assim você não pode acessar o valor dentro do método.

     1. You just have to use an arrow function in the Onclick event like this:  that.handleSort(theValue)} >{column} 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); } 

    Há um jeito muito fácil.

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

    Eu acho, .bind(this, arg1, arg2, ...) no mapa do React – é um código ruim, porque é lento! 10-50 de .bind(this) no método de renderização única – código muito lento.
    Eu corri assim:
    Método de renderização


    mapa de


    Manipulador
    var id = $(ev.target).closest('tr').data().id

    Código completo abaixo:

     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 (  {list.map(listItem =>(  ))} 
    {listItem.id} {listItem.name}
    ) } 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(, document.getElementById("react-dom"))
     table { border-collapse: collapse; } .selected td { background-color: rgba(0, 0, 255, 0.3); } 
        

    Eu recomendaria o curry over bind neste caso. Gostar,

     return (  );