React js onClick no puede pasar valor al método

776

Quiero leer las propiedades del valor del evento onClick. Pero cuando hago clic en él, veo algo como esto en la consola:

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

Mi código funciona correctamente. Cuando ejecuto, puedo ver {column}pero no puedo obtenerlo en el evento onClick.

Mi código:

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

¿Cómo puedo pasar un valor al onClickevento en React js?

4
  • 2
    posible duplicado del enlace de eventos OnClick en React.jsWiredPrairie 23/04/15 a las 0:11
  • 2
    considere usar uno mismo en lugar de eso. Eso es bastante engañoso, ya que debería ser sinónimo de "esto" (aunque no es importante para cada uno)Dan 30/06/2017 a las 14:53
  • Usando el método de enlace y el método de flecha, podemos pasar el valor al evento OnclickMerugu Prashanth 12 de septiembre de 2017 a las 7:49
  • Estuve de acuerdo con @WiredPrairie y el tipo llamado ZenMaster lo explicó con precisión y eficacia. perfectionist1 24 feb a las 14:14
1503

Forma fácil

Utilice una función de flecha:

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

Esto creará una nueva función que llama handleSortcon los parámetros correctos.

Mejor manera

Extraerlo en un subcomponente. El problema con el uso de una función de flecha en la llamada de renderizado es que creará una nueva función cada vez, lo que acaba provocando nuevas renderizaciones innecesarias.

Si crea un subcomponente, puede pasar el controlador y usar accesorios como argumentos, que luego se volverán a representar solo cuando los accesorios cambien (porque la referencia del controlador ahora nunca cambia):

Subcomponente

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

  render() {
    return (
      <th onClick={this.handleClick}>
        {this.props.column}
      </th>
    );
  }
}

Componente principal

{this.props.defaultColumns.map((column) => (
  <TableHeader
    value={column}
    onHeaderClick={this.handleSort}
  />
))}

Old Easy Way (ES5)

Use .bindpara pasar el parámetro que desea, de esta manera está vinculando la función con el contexto del componente:

return (
  <th value={column} onClick={this.handleSort.bind(this, column)}>{column}</th>
);
19
  • 7
    reaccionar da una advertencia cuando uso como su código. Cambio mi código a onClick = {that.onClick.bind (null, column)}user1924375 23/04/15 a las 9:13
  • 12
    ¿Cómo usaría esto con una <a>etiqueta donde necesita pasar el evento, para poder usarpreventDefault()Simon H 24 oct 2015 a las 9:43
  • 39
    @SimonH El evento se pasará como último argumento, después de los argumentos a través de los que pasa bind. smudge 4/11/15 a las 17:26
  • 47
    ¿No es malo para el rendimiento? ¿No se creará una nueva función en cada render? AndrewMcLagan 17 de mayo de 2016 a las 7:44
  • 14
    @AndrewMcLagan Lo es. Me pareció que esta para describir la regla y la solución de los mas potente. E. Sundin 3 de julio de 2016 a las 22:46
179

Hay buenas respuestas aquí, y estoy de acuerdo con @Austin Greco (la segunda opción con componentes separados)
Hay otra forma que me gusta, curry .
Lo que puede hacer es crear una función que acepte un parámetro (su parámetro) y devuelva otra función que acepte otro parámetro (el evento click en este caso). entonces eres libre de hacer con él lo que quieras.

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

Y lo usarás de esta manera:

<input 
    type="text" 
    onChange={this.handleChange(someParam)} 
/>

Aquí hay un ejemplo completo de tal uso:

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>

Tenga en cuenta que este enfoque no resuelve la creación de una nueva instancia en cada renderizado.
Me gusta este enfoque sobre los otros controladores en línea, ya que este es más conciso y legible en mi opinión.

Editar:
como se sugiere en los comentarios a continuación, puede almacenar en caché / memorizar el resultado de la función.

Aquí hay una implementación ingenua:

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" />
14
  • 13
    Esta debería ser la respuesta aceptada. REALMENTE fácil de implementar y no es necesario crear ningún otro componente o enlazar de manera diferente. ¡Gracias! Tamb 4 de agosto de 2017 a las 16:28
  • 32
    Se ve mejor, pero desde una perspectiva de rendimiento, sin embargo, currying no ayuda realmente, porque si llamas a handleChange dos veces, con el mismo parámetro, obtienes dos funciones que el motor JS considera objetos separados, incluso si hacen lo mismo . Así que todavía obtienes una re-renderización. En aras del rendimiento, necesitaría almacenar en caché los resultados de handleChange para obtener la ventaja de rendimiento. ComohandleChange = param => cache[param] || (e => { // the function body })travellingprog 26 de agosto de 2017 a las 1:38
  • 6
    Esta es la respuesta perfecta si sigue los consejos de @travellingprogllioor 20/12/2017 a las 10:54
  • 2
    ¿Alguien puede proporcionar un enlace o explicar cómo funciona este mecanismo de almacenamiento en caché? Gracias. Sadok Mtir 26 de enero de 2018 a las 16:02
  • 3
    Si usa curry, se creará una nueva función en cada render. El mismo problema de rendimiento ocurre cuando se pasa una función de flecha. omeralper 20 feb 2019 a las 23:58
134

Hoy en día, con ES6, creo que nos vendría bien una respuesta actualizada.

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

Básicamente, (para cualquiera que no sepa) ya onClickque espera que se le pase una función, bindfunciona porque crea una copia de una función. En su lugar, podemos pasar una expresión de función de flecha que simplemente invoca la función que queremos y la conserva this. Nunca debería necesitar vincular el rendermétodo en React, pero si por alguna razón está perdiendo thisen uno de sus métodos de componentes:

constructor(props) {
  super(props);
  this.myMethod = this.myMethod.bind(this);
}
11
  • 11
    Nunca necesitas enlazar render()porque React lo llama. En todo caso, necesita bindlos controladores de eventos, y solo cuando no esté usando flechas. Dan Abramov 2 de marzo de 2016 a las 13:46
  • 4
    Tenga en cuenta que también es mejor para pasar propsa super()o this.propsserá indefinido durante el constructor que puede prestarse a confusión. Dan Abramov 2 mar 16 a las 13:55
  • 3
    ¿Para lograr qué? Puede definir un controlador dentro del componente funcional y pasarlo. Será una función diferente en cada render, por lo que si ha determinado con un generador de perfiles que le da problemas de rendimiento (¡y solo si está realmente seguro de esto!), Considere usar una clase en su lugar. Dan Abramov 19/04/2016 a las 23:04
  • 2
    Gracias Aikeru. Ah, está bien, entonces está diciendo que si declara el método foo como una función de flecha en la clase en sí, luego haga referencia a él usando este contexto, entonces solo se crea una vez en lugar de declararlo dentro del onClick donde está creando la misma función cada vez que hace clic el botón. ¿Es eso correcto en mi forma de pensar? me-me 25 de mayo de 2018 a las 15:43
  • 2
    @zuckerburg onClick = {() => alert ('hola')} no alertará inmediatamente, ni onClick = {this.showAlert} porque en ambos casos estás pasando (no invocando) una función. aikeru 7/06/19 a las 15:37
77

[[h / t a @ E.Sundin por vincular esto en un comentario]

La respuesta principal (funciones anónimas o enlace) funcionará, pero no es la más eficaz, ya que crea una copia del controlador de eventos para cada instancia generada por la map()función.

Esta es una explicación de la forma óptima de hacerlo desde ESLint-plugin-react :

Lists of Items

A common use case of bind in render is when rendering a list, to have a separate callback per list item:

const List = props => (
      <ul>
        {props.items.map(item =>
          <li key={item.id} onClick={() => console.log(item.id)}>
            ...
          </li>
        )}
      </ul>
    );

Rather than doing it this way, pull the repeated section into its own component:

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

});

This will speed up rendering, as it avoids the need to create new functions (through bind calls) on every render.

8
  • ¿Reaccionar invoca esas funciones con call / apply, luego, bajo el capó, y evita usar bind internamente? aikeru 12/08/16 a las 15:07
  • 1
    ¿Hay alguna forma de hacer esto usando un componente sin estado? Carlos Martinez 9 de mayo de 2017 a las 13:50
  • 2
    @CarlosMartinez buen ojo, actualicé el ejemplo; deberían haber sido componentes funcionales sin estado (SFC) en primer lugar. Por lo general, si un componente no se utiliza nunca this.state, puede cambiarlo de forma segura por un SFC. Brandon 9 de mayo de 2017 a las 14:46
  • 4
    Hmm, no entiendo cómo esto es más eficaz. ¿No se invocará la función ListItem cada vez y, por lo tanto, la función _onClick se creará en cada render? Mattias Petter Johansson 19 de mayo de 2017 a las 21:04
  • 1
    Estoy lejos de ser un experto aquí, pero según tengo entendido, en el patrón 'correcto', solo hay una instancia del controlador y se le ha pasado el apoyo para cualquier instancia del componente que lo llame. En el ejemplo de vinculación (es decir, el patrón "incorrecto"), hay una instancia del controlador para cada componente instanciado. Es una especie de memoria equivalente a escribir la misma función treinta veces, escribirla una vez y llamarla donde sea necesario. Brandon 19 de mayo de 2017 a las 21:27
29

Este es mi enfoque, no estoy seguro de qué tan malo es, por favor comente

En el elemento en el que se puede hacer clic

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

y luego

handleSort(e){
    this.sortOn(e.currentTarget.getAttribute('data-column'));
}
6
  • Este es un enfoque en el que estaba pensando, se siente un poco hack pero evita crear un nuevo componente. No estoy seguro de si getAttribute es mejor o peor en comparación con la extracción en un componente separado. kamranicus 15 de marzo de 2017 a las 5:00
  • 4
    Creo que es una buena solución porque es muy simple. Pero solo funciona con valores de cadena, si desea un objeto, no funciona. Stephane L 16 de marzo de 2018 a las 16:41
  • Para que un objeto que tendría que hacer encodeURIComponent(JSON.stringify(myObj)), a continuación, analizarlo, JSON.parse(decodeURIComponent(myObj)). Para las funciones, estoy bastante seguro de que esto no funcionará sin eval o new Function (), los cuales deben evitarse. Por estas razones, no uso atributos de datos para pasar datos en React / JS. png 20/04/18 a las 22:42
  • Quiero agregar que no uso esto a menudo y solo para cosas menores. Pero generalmente solo creo un componente y le paso los datos como accesorios. Luego maneje el clic dentro de ese nuevo componente o pase una función onClick al componente. Como se explica en la respuesta de BrandonSantiago Ramirez 4 de mayo de 2018 a las 8:54
  • 1
    Se puede acceder al conjunto de datos directamente de esta manera en los navegadores modernos (incluido IE11): e.currentTarget.dataset.columngsziszi 21 oct 2018 a las 15:24
23

este ejemplo puede ser un poco diferente al suyo. pero puedo asegurarle que esta es la mejor solución que puede tener para este problema. He buscado durante días una solución que no tenga problemas de rendimiento. y finalmente se me ocurrió este.

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

ACTUALIZAR

en caso de que desee tratar con objetos que se supone que son los parámetros. que puede usar JSON.stringify(object)para convertirlo en una cadena y agregarlo al conjunto de datos.

return (
   <div>
     <h3 data-param={JSON.stringify({name:'me'})} onClick={this.handleClick}>
        {this.state.name}
     </h3>
   </div>
);
4
  • 2
    esto no funciona cuando los datos pasados ​​son un objetoSlimSim 19 de agosto de 2017 a las 18:04
  • use JSON.stringify para solucionar el problema. @SlimSim. eso debería hacer el trucohannad rehman 21/08/2017 a las 9:21
  • 1
    Si necesita usar JSON.stringify para este problema, probablemente no sea el método correcto. El proceso de encadenamiento requiere mucha memoria. KFE 3 de nov. De 2017 a las 13:54
  • 1
    en la mayoría de los casos, solo pasaría ID como parámetros y obtendría los detalles del objeto en función de esa ID de su objeto de origen. y por qué y cómo se necesita mucha memoria, sé que JSON stringify es lento, pero el clic Fn es asíncrono y no tendrá ningún efecto o 0 en dom, una vez construidohannad rehman 3 de nov. De 2017 a las 13:56
9
class extends React.Component {
    onClickDiv = (column) => {
        // do stuff
    }
    render() {
        return <div onClick={() => this.onClickDiv('123')} />
    }
}
1
  • 3
    Lo mismo: nuevo DOM en cada render. Entonces React actualizará DOM cada vez. Alex Shwarc 11/04/2017 a las 17:02
8

Simplemente crea una función como esta

  function methodName(params) {
    //the thing  you wanna do
  }

y llámalo en el lugar que necesites

 <Icon onClick = {() => { methodName(theParamsYouwantToPass);} }/>
3
  • 1
    esto está funcionando para mí, pero ¿por qué? Eduardo ML 5 de agosto a las 14:40
  • Entonces quieres que funcione, ¿verdad? Charith Jayasanka 6 de agosto a las 12:04
  • 1
    Oc 😶😶😶😶😶😶Eduardo ML 6 de agosto a las 15:32
7

Me doy cuenta de que es bastante tarde para la fiesta, pero creo que una solución mucho más simple podría satisfacer muchos casos de uso:

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

    ...

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

Supongo que también podrías usar un data-atributo.

Sencillo, semántico.

0
6

Haciendo un intento alternativo para responder la pregunta de OP, incluidas las llamadas a e.preventDefault ():

Enlace renderizado ( ES6 )

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

Función de componente

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

Una opción más que no involucra .bindo ES6 es usar un componente secundario con un controlador para llamar al controlador principal con los accesorios necesarios. Aquí hay un ejemplo (y a continuación se muestra un enlace al ejemplo de trabajo):

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

La idea básica es que el componente padre pase la onClickfunción a un componente hijo. El componente hijo llama a la onClickfunción y puede acceder a cualquiera que se le propspase (y el event), lo que le permite usar cualquier eventvalor u otros accesorios dentro de la onClickfunción principal.

Aquí hay una demostración de CodePen que muestra este método en acción.

5

Simplemente puede hacerlo si está utilizando 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>
     );
   }
 }
4

La implementación de mostrar el recuento total de un objeto pasando el recuento como un parámetro de los componentes principales a los subcomponentes, como se describe a continuación.

Aquí 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 (
      <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;

Y aqui esta 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>
    )
  }
};

Intente implementar lo anterior y obtendrá un escenario exacto de cómo funcionan los parámetros de paso en reactjs en cualquier método DOM.

4

Hay un par de formas de pasar parámetros en los controladores de eventos, algunas son las siguientes.

Puede usar una función de flecha para envolver un controlador de eventos y pasar parámetros:

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

El ejemplo anterior es equivalente a llamar .bindo puede llamar explícitamente a bind.

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

Aparte de estos dos enfoques, también puede pasar argumentos a una función que se define como una función de curry.

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

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

La mejor solución con React> = 16

La forma más limpia que he encontrado para llamar a funciones con múltiples parámetros en onClick, onChange, etc.sin usar funciones en línea es usar el dataatributo personalizado disponible en React 16 y versiones superiores.

const App = () => {
  const onClick = (e) => {
    const value1 = e.currentTarget.getAttribute("data-value1")
    const value2 = e.currentTarget.getAttribute("data-value2")
    const value2 = e.currentTarget.getAttribute("data-value2")
    console.log("Values1", value1)
    console.log("Values2", value2)
    console.log("Values3", value3)
  }
  return (
    <button onClick={onClick} data-value1="a" data-value2="b" data-value3="c" />
  )
}

El ejemplo anterior es para un componente funcional, pero la implementación es bastante similar incluso en los componentes de la clase.

Este enfoque no produce re-renderizaciones innecesarias porque no está utilizando funciones en línea y evita la molestia de enlazar con this.

Le permite pasar tantos valores como le gustaría usar en su función.

Si está pasando valores como accesorios a sus hijos para que los usen en el onClick del componente hijo, también puede usar este enfoque allí, sin crear una función contenedora.

También funciona con una matriz de objetos, en los casos en que desee pasar la identificación del objeto al onClick, como se muestra a continuación.

const App = () => {
  const [arrState, setArrState] = useState(arr)

  const deleteContent = (e) => {
    const id = e.currentTarget.getAttribute("data-id")
    const tempArr = [...arrState]
    const filteredArr = tempArr.filter((item) => item.id !== id)
    setArrState(filteredArr)
  }

  return (
    <div>
      {arrState.map((item) => (
        <React.Fragment key={item.id}>
          <p>{item.content}</p>
          <button onClick={deleteContent} data-id={item.id}>
            Delete
          </button>
        </React.Fragment>
      ))}
    </div>
  )
}
4

Escribí un componente contenedor que se puede reutilizar para este propósito que se basa en las respuestas aceptadas aquí. Sin embargo, si todo lo que necesita hacer es pasar una cadena, simplemente agregue un atributo de datos y léalo desde e.target.dataset (como algunos otros han sugerido). De forma predeterminada, mi contenedor se vinculará a cualquier accesorio que sea una función y comience con 'on' y automáticamente pasará el accesorio de datos a la persona que llama después de todos los demás argumentos del evento. Aunque no lo he probado para el rendimiento, le dará la oportunidad de evitar crear la clase usted mismo, y se puede usar así:

const DataButton = withData('button')

const DataInput = withData('input');

o para Componentes y funciones

const DataInput = withData(SomeComponent);

o si lo prefieres

const DataButton = withData(<button/>)

declare que Fuera de su contenedor (cerca de sus importaciones)

Aquí está el uso en un contenedor:

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

Aquí está el código contenedor '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;
    }, {});
}
3

Tengo a continuación 3 sugerencias para esto en JSX onClick Events -

  1. En realidad, no necesitamos usar la función .bind () o Arrow en nuestro código. Puede usarlo de manera simple en su código.

  2. También puede mover el evento onClick de th (o ul) a tr (o li) para mejorar el rendimiento. Básicamente, tendrá un número n de "Event Listeners" para su elemento 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>
    

    // Y puede acceder item.idal onItemClickmétodo como se muestra a continuación:

    onItemClick = (event) => {
       console.log(e.target.getAttribute("item.id"));
    }
    
  3. Estoy de acuerdo con el enfoque mencionado anteriormente para crear un componente React separado para ListItem y List. Este código de creación se ve bien, sin embargo, si tiene 1000 de li, se crearán 1000 Event Listeners. Por favor, asegúrese de no tener mucho detector de eventos.

    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>
            );
        }
    }
    
1
  • 1
    Esto no funciona cuando los datos que necesita pasar son un objeto. El atributo solo funcionará con cadenas. También leer desde el dom a través del atributo get es probablemente una operación más cara. SlimSim 19/08/2017 a las 17:38
3

He agregado código para pasar el valor del evento onclick al método de dos maneras. 1. usando el método de vinculación 2. usando el método de flecha (=>). ver los métodos handleort1 y handleort

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

A continuación se muestra el ejemplo que transfiere valor al evento onClick.

Usé la sintaxis es6. recuerde que la función de flecha del componente de clase no se vincula automáticamente, por lo que se vincula explícitamente en el constructor.

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

Supongo que tendrás que vincular el método a la instancia de clase de React. Es más seguro usar un constructor para vincular todos los métodos en React. En su caso, cuando pasa el parámetro al método, el primer parámetro se usa para vincular el contexto 'this' del método, por lo que no puede acceder al valor dentro del método.

2

Usando la función de flecha:

Debe instalar stage-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; 
2
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);
}
2

Hay una forma muy sencilla.

 onClick={this.toggleStart('xyz')} . 
  toggleStart= (data) => (e) =>{
     console.log('value is'+data);  
 }
0
2
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>
    );
  }
}
1
  • 4
    Si bien este código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo a la pregunta para los lectores en el futuro, y es posible que esas personas no conozcan los motivos de su sugerencia de código. Shree 10/03/19 a las 7:13
2

Saliendo de la nada a esta pregunta, pero creo .bindque funcionará. Busque el código de muestra a continuación.

const handleClick = (data) => {
    console.log(data)
}

<button onClick={handleClick.bind(null, { title: 'mytitle', id: '12345' })}>Login</button>
0

Hay 3 formas de manejar esto: -

  1. Vincular el método en el constructor como: -

    export class HeaderRows extends Component {
       constructor() {
           super();
           this.handleSort = this.handleSort.bind(this);
       }
    }
    
  2. Utilice la función de flecha mientras la crea como: -

    handleSort = () => {
        // some text here
    }
    
  3. La tercera forma es esta: -

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

Puedes usar tu código así:

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

Aquí e es para el objeto de evento, si desea utilizar métodos de evento como preventDefault()en su función de control o desea obtener el valor de destino o el nombre como e.target.name.

0

Hubo muchas consideraciones de rendimiento, todas en el vacío.
El problema con estos controladores es que debe currizarlos para incorporar el argumento que no puede nombrar en los accesorios.
Esto significa que el componente necesita un controlador para todos y cada uno de los elementos en los que se puede hacer clic. Aceptemos que para algunos botones esto no es un problema, ¿verdad?
El problema surge cuando maneja datos tabulares con docenas de columnas y miles de filas. Allí se nota el impacto de crear tantos controladores.

El hecho es que solo necesito uno.
Configuré el controlador en el nivel de la tabla (o UL u OL ...), y cuando ocurre el clic puedo decir cuál fue la celda en la que se hizo clic utilizando los datos disponibles desde siempre en el objeto de evento:

nativeEvent.target.tagName
nativeEvent.target.parentElement.tagName 
nativeEvent.target.parentElement.rowIndex
nativeEvent.target.cellIndex
nativeEvent.target.textContent

Utilizo los campos de nombre de etiqueta para verificar que el clic ocurrió en un elemento válido, por ejemplo, ignorar los clics en THs o pies de página.
RowIndex y cellIndex dan la ubicación exacta de la celda en la que se hizo clic.
El contenido del texto es el texto de la celda en la que se hizo clic.

De esta manera, no necesito pasar los datos de la celda al controlador, puede autoservicio.
Si necesito más datos, datos que no se mostrarán, puedo usar el atributo del conjunto de datos o elementos ocultos.
Con una simple navegación DOM, todo está a mano.
Esto se ha utilizado en HTML desde siempre, ya que las PC eran mucho más fáciles de atascar.

1
  • Plantea un tema muy importante: ¡Consideración de rendimiento! perfectionist1 24 feb a las 14:18
0

Cuando se trabaja con una función en lugar de una clase, en realidad es bastante fácil.


    const [breakfastMain, setBreakFastMain] = useState("Breakfast");

const changeBreakfastMain = (e) => {
    setBreakFastMain(e.target.value);
//sometimes "value" won't do it, like for text, etc. In that case you need to 
//write 'e.target/innerHTML'
 }

<ul  onClick={changeBreakfastMain}>
   <li>
"some text here"
   </li>
<li>
"some text here"
   </li>
</ul>
0

Lo haría así:

const HeaderRows = props => {
    const handleSort = value => () => {

    }

    return <tr>
        {props.defaultColumns.map((column, i) =>
            <th key={i} onClick={handleSort(column)}>{column}</th>)}
        {props.externalColumns.map((column, i) => {
            // Multi dimension array - 0 is column name
            const externalColumnName = column[0]
            return (<th key={i}>{externalColumnName}</th>)
        })}
    </tr>
}