Controlador de eventos React JS onClick

120

yo tengo

var TestApp = React.createClass({
      getComponent: function(){
          console.log(this.props);
      },
      render: function(){
        return(
             <div>
             <ul>
                <li onClick={this.getComponent}>Component 1</li>
             </ul>
             </div>
        );
      }
});
React.renderComponent(<TestApp />, document.body);

Quiero colorear el fondo del elemento de la lista en el que se hizo clic. ¿Cómo puedo hacer esto en React?

Algo como

$('li').on('click', function(){
    $(this).css({'background-color': '#ccc'});
});
usuario544079
fuente

Respuestas:

95

Por qué no:

onItemClick: function (event) {

    event.currentTarget.style.backgroundColor = '#ccc';

},

render: function() {
    return (
        <div>
            <ul>
                <li onClick={this.onItemClick}>Component 1</li>
            </ul>
        </div>
    );
}

Y si desea ser más reactivo al respecto, es posible que desee establecer el elemento seleccionado como el estado de su componente React que lo contiene, luego haga referencia a ese estado para determinar el color del elemento dentro de render:

onItemClick: function (event) {

    this.setState({ selectedItem: event.currentTarget.dataset.id });
    //where 'id' =  whatever suffix you give the data-* li attribute
},

render: function() {
    return (
        <div>
            <ul>
                <li onClick={this.onItemClick} data-id="1" className={this.state.selectedItem == 1 ? "on" : "off"}>Component 1</li>
                <li onClick={this.onItemClick} data-id="2" className={this.state.selectedItem == 2 ? "on" : "off"}>Component 2</li>
                <li onClick={this.onItemClick} data-id="3" className={this.state.selectedItem == 3 ? "on" : "off"}>Component 3</li>
            </ul>
        </div>
    );
},

Querría poner esos <li>s en un bucle, y necesita hacer que los estilos li.ony li.offestablezcan su background-color.

ericsoco
fuente
La manipulación DOM manual en React es un anti-patrón que solo conduce a más problemas. Evite cosas como a event.currentTarget.style.backgroundColor = '#ccc';menos que realmente comprenda lo que está haciendo (la mayoría de las veces, mientras integra widgets de terceros).
Emile Bergeron
61

Dos formas en las que puedo pensar son

var TestApp = React.createClass({
    getComponent: function(index) {
        $(this.getDOMNode()).find('li:nth-child(' + index + ')').css({
            'background-color': '#ccc'
        });
    },
    render: function() {
        return (
            <div>
              <ul>
                <li onClick={this.getComponent.bind(this, 1)}>Component 1</li>
                <li onClick={this.getComponent.bind(this, 2)}>Component 2</li>
                <li onClick={this.getComponent.bind(this, 3)}>Component 3</li>
              </ul>
            </div>
        );
    }
});
React.renderComponent(<TestApp /> , document.getElementById('soln1'));

Este es mi favorito personal.

var ListItem = React.createClass({
    getInitialState: function() {
        return {
            isSelected: false
        };
    },
    handleClick: function() {
        this.setState({
            isSelected: true
        })
    },
    render: function() {
        var isSelected = this.state.isSelected;
        var style = {
            'background-color': ''
        };
        if (isSelected) {
            style = {
                'background-color': '#ccc'
            };
        }
        return (
            <li onClick={this.handleClick} style={style}>{this.props.content}</li>
        );
    }
});

var TestApp2 = React.createClass({
    getComponent: function(index) {
        $(this.getDOMNode()).find('li:nth-child(' + index + ')').css({
            'background-color': '#ccc'
        });
    },
    render: function() {
        return (
            <div>
             <ul>
              <ListItem content="Component 1" />
              <ListItem content="Component 2" />
              <ListItem content="Component 3" />
             </ul>
            </div>
        );
    }
});
React.renderComponent(<TestApp2 /> , document.getElementById('soln2'));

Aquí hay una DEMO

Espero que esto ayude.

Dhiraj
fuente
8
No se recomienda aplicar el enlace dentro de la función de renderizado, ya que lo hará cada vez que se renderice el componente. puede moverlo a alguna función que se ejecute al comienzo del ciclo de vida
jony89
1
@ jony89 estuvo de acuerdo si .bindno toma un parámetro adicional. Pero en el primer caso lo hace. No creo que haya otra manera
Dhiraj
1
Hay, crear tres funciones diferentes (que se crearon a partir del resultado de getComponent.bind (this, 1)), aunque esa definición puede ser una decisión (lo haría para 2-3 componentes, no para 20, a menos que sea realmente un problema de rendimiento y fácil de crear de forma dinámica).
jony89
38

Así es como se define un controlador de eventos react onClick , que respondía al título de la pregunta ... usando la sintaxis es6

import React, { Component } from 'react';

export default class Test extends Component {
  handleClick(e) {
    e.preventDefault()
    console.log(e.target)
  }

  render() {
    return (
      <a href='#' onClick={e => this.handleClick(e)}>click me</a>
    )
  }
}
svnm
fuente
9
Ni bindlas funciones de flecha ni deben usarse dentro de los rendermétodos porque dan como resultado la creación de una nueva función cada vez. Esto tiene el efecto de cambiar el estado del componente, y los componentes con estado cambiado siempre se vuelven a renderizar. Para uno solo aesto no es gran cosa. Para las listas generadas con elementos en los que se puede hacer clic, esto se convierte en un gran problema muy rápidamente. Es por eso que se advierte específicamente contra esto.
hippietrail
18

Utilice ECMA2015. Las funciones de flecha hacen que "esto" sea mucho más intuitivo.

import React from 'react';


class TestApp extends React.Component {
   getComponent(e, index) {
       $(e.target).css({
           'background-color': '#ccc'
       });
   }
   render() {
       return (
           <div>
             <ul>
               <li onClick={(e) => this.getComponent(e, 1)}>Component 1</li>
               <li onClick={(e) => this.getComponent(e, 2)}>Component 2</li>
               <li onClick={(e) => this.getComponent(e, 3)}>Component 3</li>
             </ul>
           </div>
       );
   }
});
React.renderComponent(<TestApp /> , document.getElementById('soln1'));`
cortador
fuente
2
indexno hace nada aqui?
norteamericano
@northamerican - No, solo está ahí para agregar algo de claridad a los parámetros
itcropper
5
En realidad, esto es malo para el rendimiento, ya que crea una nueva función en cada render. Ver: stackoverflow.com/questions/36677733/…
Jochie Nabuurs
1
¡Y por favor no uses jQuery dentro de React si no es necesario!
Emile Bergeron
13

Si está utilizando ES6, aquí hay un código de ejemplo simple:

import React from 'wherever_react_is';

class TestApp extends React.Component {

  getComponent(event) {
      console.log('li item clicked!');
      event.currentTarget.style.backgroundColor = '#ccc';
  }

  render() {
    return(
       <div>
         <ul>
            <li onClick={this.getComponent.bind(this)}>Component 1</li>
         </ul>
       </div>
    );
  }
}

export default TestApp;

En los cuerpos de clase de ES6, las funciones ya no requieren la palabra clave 'función' y no necesitan estar separadas por comas. También puede usar la sintaxis => si lo desea.

Aquí tienes un ejemplo con elementos creados dinámicamente:

import React from 'wherever_react_is';

class TestApp extends React.Component {

constructor(props) {
  super(props);

  this.state = {
    data: [
      {name: 'Name 1', id: 123},
      {name: 'Name 2', id: 456}
    ]
  }
}

  getComponent(event) {
      console.log('li item clicked!');
      event.currentTarget.style.backgroundColor = '#ccc';
  }

  render() {        
       <div>
         <ul>
         {this.state.data.map(d => {
           return(
              <li key={d.id} onClick={this.getComponent.bind(this)}>{d.name}</li>
           )}
         )}
         </ul>
       </div>
    );
  }
}

export default TestApp;

Tenga en cuenta que cada elemento creado dinámicamente debe tener una 'clave' de referencia única.

Además, si desea pasar el objeto de datos real (en lugar del evento) a su función onClick, deberá pasarlo a su enlace. Por ejemplo:

Nueva función onClick:

getComponent(object) {
    console.log(object.name);
}

Pasando el objeto de datos:

{this.state.data.map(d => {
    return(
      <li key={d.id} onClick={this.getComponent.bind(this, d)}>{d.name}</li>
    )}
)}
dirtigr00vz
fuente
Estoy tratando de construir mis elementos li dinámicos y luego esto se vuelve indefinido y, por lo tanto, la función onClick arroja un error.
aterrizó
1
Acabo de encontrar una respuesta similar en la que necesita usar .bind (esto)); al final de la función anónima, ya que aquí se refiere a la ventana hasta que realice el enlace ...
aterrizó
6

El manejo de eventos con elementos React es muy similar al manejo de eventos en elementos DOM. Hay algunas diferencias sintácticas:

  • Los eventos de React se nombran usando camelCase, en lugar de minúsculas.
  • Con JSX, pasa una función como controlador de eventos, en lugar de una cadena.

Entonces, como se menciona en la documentación de React , son bastante similares al HTML normal cuando se trata de Manejo de eventos, pero los nombres de eventos en React usando camelcase, porque no son realmente HTML, son JavaScript, además, pasas la función mientras pasamos la llamada a la función en un formato de cadena para HTML, son diferentes, pero los conceptos son bastante similares ...

Mire el ejemplo a continuación, preste atención a la forma en que el evento se pasa a la función:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}
Alireza
fuente
3

import React from 'react';

class MyComponent extends React.Component {

  getComponent(event) {
      event.target.style.backgroundColor = '#ccc';
      
      // or you can write
      //arguments[0].target.style.backgroundColor = '#ccc';
  }

  render() {
    return(
       <div>
         <ul>
            <li onClick={this.getComponent.bind(this)}>Component 1</li>
         </ul>
       </div>
    );
  }
}

export { MyComponent };  // use this to be possible in future imports with {} like: import {MyComponent} from './MyComponent'
export default MyComponent;

Jalaleddin Hosseini
fuente
Esto parece esencialmente idéntico a la respuesta de 11 puntos y resucita una pregunta bonita, o ¿por qué?
Dave Newton
2

class FrontendSkillList extends React.Component {
  constructor() {
    super();
    this.state = { selectedSkill: {} };
  }
  render() {
    return (
      <ul>
        {this.props.skills.map((skill, i) => (
            <li
              className={
                this.state.selectedSkill.id === skill.id ? "selected" : ""
              }
              onClick={this.selectSkill.bind(this, skill)}
              style={{ cursor: "pointer" }}
              key={skill.id}
            >
            {skill.name}
            </li>
        ))}
      </ul>
    );
  }

  selectSkill(selected) {
    if (selected.id !== this.state.selectedSkill.id) {
      this.setState({ selectedSkill: selected });
    } else {
      this.setState({ selectedSkill: {} });
    }
  }
}

const data = [
  { id: "1", name: "HTML5" },
  { id: "2", name: "CSS3" },
  { id: "3", name: "ES6 & ES7" }
];
const element = (
  <div>
    <h1>Frontend Skill List</h1>
    <FrontendSkillList skills={data} />
  </div>
);
ReactDOM.render(element, document.getElementById("root"));
.selected {
  background-color: rgba(217, 83, 79, 0.8);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

@ user544079 Espero que esta demostración pueda ayudar :) Recomiendo cambiar el color de fondo alternando el nombre de la clase.

dabeng
fuente
2

import React from 'react';

class MyComponent extends React.Component {

  getComponent(event) {
      event.target.style.backgroundColor = '#ccc';
      
      // or you can write
      //arguments[0].target.style.backgroundColor = '#ccc';
  }

  render() {
    return(
       <div>
         <ul>
            <li onClick={this.getComponent.bind(this)}>Component 1</li>
         </ul>
       </div>
    );
  }
}

export { MyComponent };  // use this to be possible in future imports with {} like: import {MyComponent} from './MyComponent'
export default MyComponent;

Jakkapan Sitthikan
fuente
¿Puede proporcionar más contexto a este código explicando cómo esto podría solucionar el problema?
MEDZ
1

Puede hacer uso del método React.createClone. Crea tu elemento, luego crea un clon de él. Durante la creación del clon, puedes inyectar accesorios. Inyectar un método onClick: prop como este

{ onClick : () => this.changeColor(originalElement, index) }

el método changeColor establecerá el estado con el duplicado, lo que le permitirá dejar de establecer el color en el proceso.

render()
  {
    return(
      <ul>

        {this.state.items.map((val, ind) => {
          let item = <li key={ind}>{val}</li>;
          let props = { 
            onClick: () => this.Click(item, ind),
            key : ind,
            ind
          }
          let clone = React.cloneElement(item, props, [val]);
          return clone;
        })}

      </ul>
    )
  }

Eddie D
fuente
La clonación es totalmente innecesaria.
Emile Bergeron
-17

Este es un patrón React no estándar (pero no tan infrecuente) que no usa JSX, sino que pone todo en línea. Además, es Coffeescript.

La 'forma de reacción' para hacer esto sería con el propio estado del componente:

( c = console.log.bind console)

mock_items: [
    {
        name: 'item_a'
        uid: shortid()
    }
    {
        name: 'item_b'
        uid: shortid()
    }
    {
        name: 'item_c'
        uid: shortid()
    }
]
getInitialState: ->
    lighted_item: null
render: ->
    div null,
        ul null,
            for item, idx in @mock_items
                uid = item.uid
                li
                    key: uid
                    onClick: do (idx, uid) =>
                        (e) =>
                            # justf to illustrate these are bound in closure by the do lambda,
                            c idx
                            c uid
                            @setState
                                lighted_item: uid
                    style:
                        cursor: 'pointer'
                        background: do (uid) =>
                            c @state.lighted_item
                            c 'and uid', uid
                            if @state.lighted_item is uid then 'magenta' else 'chartreuse'
                        # background: 'chartreuse'
                    item.name

Este ejemplo funciona, lo probé localmente. Puede consultar este código de ejemplo exactamente en mi github . Originalmente, el env era solo local para mis propios fines de I + D de pizarra, pero lo publiqué en Github para esto. Es posible que se escriba en algún momento, pero puede consultar el compromiso del 8 de septiembre de 2016 para ver esto.

De manera más general, si desea ver cómo funciona este patrón CS / no-JSX para React, consulte algunos trabajos recientes aquí . Es posible que tenga tiempo para implementar completamente un POC para esta idea de aplicación, cuya pila incluye NodeJS, Primus, Redis y React.

Wylie Kulik
fuente
el fondo no necesita ser una dolambda: esta expresión también funciona:background: if @state.lighted_item is uid then 'magenta' else 'chartreuse'
Wylie Kulik
hola ¿cómo puedo ver onclick en la consola del navegador?
Muneem Habib
12
¿Por qué usarías CoffeeScript en una respuesta a una pregunta que no la menciona de ninguna manera? No tiene ningún sentido y probablemente puede hacer que la respuesta sea más difícil de leer para el autor de la pregunta, ya que es posible que no sepa / no le guste CoffeeScript. Votar en contra, obviamente.
macbem
7
No, pero es algo construido sobre el lenguaje, no es absolutamente estándar y requiere instalación y compilación. Fue realmente una mala elección escribir su respuesta en coffeescript cuando hay CERO indicios de que están usando coffeescript en su proyecto
TheRealMrCrowley
4
Coffeescript es solo una capa encima de js. FTFY.
machineghost