javascript - pass - react onclick event and parameter
React js onClick no puede pasar valor al método (26)
Forma fácil
Use una función de flecha:
return (
<th value={column} onClick={() => this.handleSort(column)}>{column}</th>
);
Esto creará una nueva función que llama a
handleSort
con 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 render es que creará una nueva función cada vez, lo que termina causando re-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
.bind
para pasar el parámetro que desee:
return (
<th value={column} onClick={that.handleSort.bind(that, column)}>{column}</th>
);
Quiero leer las propiedades del valor del evento onClick. Pero cuando hago clic en él, veo algo así 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 evento
onClick
en React js?
Recomendaría curry over bind en este caso. Me gusta,
return (
<th value={column} onClick={this.handleSort.curry(column)} {column}</th>
);
A continuación se muestra el ejemplo que pasa valor en el 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>
);
}
}
Aquí hay buenas respuestas, y estoy de acuerdo con @Austin Greco (la segunda opción con componentes separados)
Hay otra forma que me gusta,
currying
.
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 lo que quieras.
ES5:
return (
<th value={column} onClick={()=>this.handleSort(column)} >{column}</th>
);
ES6:
constructor(props) {
super(props);
this.myMethod = this.myMethod.bind(this);
}
Y lo usarás de esta manera:
handleChange(param) { // param is the argument you passed to the function
return function (e) { // e is the event object that returned
};
}
Aquí hay un ejemplo completo de tal uso:
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)}
/>
Tenga en cuenta que este enfoque no resuelve la creación de una nueva instancia en cada render.
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:
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>
Creo que
.bind(this, arg1, arg2, ...)
en el mapa de React es un código incorrecto porque es lento.
10-50 de
.bind(this)
en un método de renderizado simple: código muy lento.
Lo arreglo así:
Método de procesamiento
<tbody onClick={this.handleClickRow}>
mapa de
<tr data-id={listItem.id}>
Entrenador de animales
var id = $(ev.target).closest(''tr'').data().id
Código completo a continuación:
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>
Encontré la solución de pasar el parámetro como atributo de una etiqueta bastante razonable.
Sin embargo tiene limitaciones:
- No funciona correctamente cuando el elemento de la lista tiene otras etiquetas (por lo tanto, event.target puede ser diferente al previsto)
- Param podría ser solo una cadena. Requiere serialización y deserialización.
Por eso se me ocurrió esta biblioteca: react-event-param
Eso:
- Resuelve el problema de los niños buscando el atributo necesario en los padres cuando sea necesario
- Serializa y deserializa automáticamente param
- Encapsula la lógica de configuración y obtención. No es necesario meterse con los nombres de los parámetros
Ejemplo de uso:
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;
Escribí un componente contenedor que puede reutilizarse 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 han sugerido algunos otros). Por defecto, mi contenedor se unirá a cualquier accesorio que sea una función y comience con ''on'' y automáticamente pasará el accesorio de datos al llamador después de todos los otros argumentos de evento. Aunque no lo he probado para el rendimiento, le dará la oportunidad de evitar crear la clase usted mismo, y puede usarse 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/>)
declarar 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 del 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;
}, {});
}
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ó esta.
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.
puede usar
JSON.stringify(object)
para convertirlo en cadena y agregarlo al conjunto de datos.
return (
<div>
<h3 data-param={JSON.stringify({name:''me''})} onClick={this.handleClick}>
{this.state.name}
</h3>
</div>
);
Este es mi enfoque, no estoy seguro de lo malo que es, por favor comente
En el elemento cliqueable
return (
<th value={column} onClick={that.handleSort} data-column={column}> {column}</th>
);
y entonces
handleSort(e){
this.sortOn(e.currentTarget.getAttribute(''data-column''));
}
Hacer un intento alternativo para responder la pregunta de OP, incluidas las llamadas e.preventDefault ():
Enlace representado ( ES6 )
<a href="#link" onClick={(e) => this.handleSort(e, ''myParam'')}>
Función componente
handleSort = (e, param) => {
e.preventDefault();
console.log(''Sorting by: '' + param)
}
Hay 3 formas de manejar esto: -
-
Enlace el método en constructor como: -
export class HeaderRows extends Component { constructor() { super(); this.handleSort = this.handleSort.bind(this); } }
-
Use la función de flecha mientras la crea como: -
handleSort = () => { // some text here }
-
La tercera forma es esta: -
<th value={column} onClick={() => that.handleSort} >{column}</th>
Hay una manera muy fácil.
onClick={this.toggleStart(''xyz'')} .
toggleStart= (data) => (e) =>{
console.log(''value is''+data);
}
He agregado código para pasar el valor del evento onclick al método de dos maneras. 1) usando el método de enlace 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>);
}
});
Hoy en día, con ES6, creo que podríamos usar una respuesta actualizada.
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);
Básicamente, (para cualquiera que no sepa) ya que
onClick
espera que se le pase una función,
bind
funciona 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 deseamos y la conserva.
Nunca debería necesitar vincular el método de
render
en React, pero si por alguna razón está perdiendo
this
en uno de sus métodos componentes:
<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" />
La implementación muestra el recuento total de un objeto pasando el recuento como 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 aquí está
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.
Me doy cuenta de que esto 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ía usar un atributo de
data-
.
Sencillo, semántico.
Puedes usar tu código así:
<th value={column} onClick={(e) => that.handleSort(e, column)} >{column}</th>
Aquí e es para objeto de evento, si desea utilizar métodos de evento como
preventDefault()
en su función de control o desea obtener un valor o nombre de
e.target.name
como
e.target.name
.
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>
);
}
}
Supongo que tendrás que vincular el método a la instancia de clase de React. Es más seguro usar un constructor para unir 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.
Tengo más de 3 sugerencias para esto en JSX onClick Events:
-
En realidad, no necesitamos usar la función .bind () o Arrow en nuestro código. Puedes usarlo fácilmente en tu código.
-
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 "Oyentes de eventos" 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 puedes acceder a
item.id
en el métodoonItemClick
como se muestra a continuación:onItemClick = (event) => { console.log(e.target.getAttribute("item.id")); }
-
Estoy de acuerdo con el enfoque mencionado anteriormente para crear un componente React separado para ListItem y List. Este código de marca se ve bien, sin embargo, si tiene 1000 de li, se crearán 1000 oyentes de eventos. Asegúrate de no tener mucho oyente 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> ); } }
Una opción más que no involucra
.bind
o ES6 es usar un componente hijo con un controlador para llamar al controlador principal con los accesorios necesarios.
Aquí hay un ejemplo (y un enlace al ejemplo de trabajo está abajo):
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 primario pase la función
onClick
a un componente secundario.
El componente hijo llama a la función
onClick
y puede acceder a cualquier
props
pase (y al
event
), lo que le permite usar cualquier valor de
event
u otro accesorio dentro de la función
onClick
del padre.
Aquí hay una demostración de CodePen que muestra este método en acción.
Usando la función de flecha:
Debe instalar la etapa 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;
[[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 función
map()
.
Esta es una explicación de la forma óptima de hacerlo desde ESLint-plugin-react :
Listas de artículos
Un caso de uso común de bind en render es cuando se renderiza una lista, para tener una devolución de llamada separada por elemento de la lista:
const List = props => (
<ul>
{props.items.map(item =>
<li key={item.id} onClick={() => console.log(item.id)}>
...
</li>
)}
</ul>
);
En lugar de hacerlo de esta manera, arrastre la sección repetida a su propio componente:
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>
);
});
Esto acelerará la representación, ya que evita la necesidad de crear nuevas funciones (a través de llamadas de enlace) en cada representación.
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>
);
}
}
class extends React.Component {
onClickDiv = (column) => {
// do stuff
}
render() {
return <div onClick={() => this.onClickDiv(''123'')} />
}
}
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);
}