Gestionnaire d'événements React JS onClick

120

j'ai

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

Je souhaite colorer l'arrière-plan de l'élément de liste cliqué. Comment puis-je faire cela dans React?

Quelque chose comme

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

Réponses:

95

Pourquoi pas:

onItemClick: function (event) {

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

},

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

Et si vous voulez être plus réactif à ce sujet, vous pouvez définir l'élément sélectionné comme l'état de son composant React contenant, puis référencer cet état pour déterminer la couleur de l'élément dans 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>
    );
},

Vous voudriez mettre ces <li>s dans une boucle, et vous devez faire en sorte que les styles li.onet li.offdéfinissent votre background-color.

ericsoco
la source
La manipulation manuelle du DOM dans React est un anti-pattern qui ne fait qu'engendrer plus de problèmes. Évitez les choses comme event.currentTarget.style.backgroundColor = '#ccc';si vous ne comprenez pas vraiment ce que vous faites (la plupart du temps, tout en intégrant des widgets tiers).
Emile Bergeron le
61

Deux façons dont je peux penser sont

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

Ceci est mon préféré.

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

Voici une DEMO

J'espère que ça aide.

Dhiraj
la source
8
Il n'est pas recommandé d'appliquer la liaison dans la fonction de rendu car elle le fera à chaque fois que le composant est rendu. vous pouvez le déplacer vers une fonction qui s'exécute au début du cycle de vie
jony89
1
@ jony89 est d'accord si .bindne prend pas de paramètre supplémentaire. Mais dans le premier cas, c'est le cas. Je ne pense pas qu'il y ait un autre moyen
Dhiraj
1
Il y a, créez trois fonctions différentes (créées par le résultat de getComponent.bind (this, 1)), bien que cela puisse être une décision (le ferait pour 2-3 composants, pas pour 20 - à moins que ce ne soit vraiment un problème de performances et facile à créer dynamiquement).
jony89
38

Voici comment définir un gestionnaire d'événements react onClick , qui répondait au titre de la question ... en utilisant la syntaxe 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
la source
9
Ni les bindfonctions fléchées ne doivent être utilisées dans les renderméthodes car elles entraînent la création d'une nouvelle fonction à chaque fois. Cela a pour effet de changer l'état du composant et les composants dont l'état a changé sont toujours rendus. Pour un célibataire, ace n'est pas grave. Pour les listes générées avec des éléments cliquables, cela devient très rapidement un gros problème. C'est pourquoi il est spécifiquement mis en garde contre.
hippietrail
18

Utilisez ECMA2015. Les fonctions fléchées rendent "ceci" beaucoup plus intuitif.

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'));`
itcropper
la source
2
indexne fait rien ici?
2016 nord-américain
@northamerican - Non, c'est juste là pour ajouter de la clarté aux paramètres
itcropper
5
Ceci est en fait mauvais pour les performances car cela crée une nouvelle fonction à chaque rendu. Voir: stackoverflow.com/questions/36677733/…
Jochie Nabuurs
1
Et n'utilisez pas jQuery dans React si vous n'êtes pas obligé de le faire!
Emile Bergeron le
13

Si vous utilisez ES6, voici un exemple de code 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;

Dans les corps de classe ES6, les fonctions n'ont plus besoin du mot-clé 'function' et elles n'ont pas besoin d'être séparées par des virgules. Vous pouvez également utiliser la syntaxe => si vous le souhaitez.

Voici un exemple avec des éléments créés dynamiquement:

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;

Notez que chaque élément créé dynamiquement doit avoir une «clé» de référence unique.

De plus, si vous souhaitez passer l'objet de données réel (plutôt que l'événement) dans votre fonction onClick, vous devrez le transmettre dans votre liaison. Par exemple:

Nouvelle fonction onClick:

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

Passer l'objet de données:

{this.state.data.map(d => {
    return(
      <li key={d.id} onClick={this.getComponent.bind(this, d)}>{d.name}</li>
    )}
)}
dirtigr00vz
la source
J'essaye de construire mes éléments li dynamiques et cela voit devenir indéfini et la fonction onClick génère donc une erreur.
atterri le
1
Je viens de trouver une réponse similaire où vous devez utiliser .bind (this)); à la fin de la fonction anonyme car cela se réfère ici à la fenêtre jusqu'à ce que vous fassiez la liaison ...
atterri le
6

La gestion des événements avec des éléments React est très similaire à la gestion des événements sur les éléments DOM. Il existe quelques différences syntaxiques:

  • Les événements React sont nommés en utilisant camelCase, plutôt qu'en minuscules.
  • Avec JSX, vous passez une fonction en tant que gestionnaire d'événements, plutôt qu'une chaîne.

Donc, comme mentionné dans la documentation React , ils sont assez similaires au HTML normal en ce qui concerne la gestion des événements, mais les noms d'événements dans React utilisant camelcase, car ils ne sont pas vraiment HTML, ils sont JavaScript, aussi, vous passez la fonction pendant que nous passons l'appel de fonction dans un format de chaîne pour HTML, ils sont différents, mais les concepts sont assez similaires ...

Regardez l'exemple ci-dessous, faites attention à la façon dont l'événement est passé à la fonction:

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

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}
Alireza
la source
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
la source
Cela semble essentiellement identique à la réponse en 11 points et ressuscite une jolie - ou une question - pourquoi?
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 J'espère que cette démo pourra vous aider :) Je recommande de changer la couleur d'arrière-plan en basculant le nom de classe.

dabeng
la source
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
la source
pouvez-vous fournir plus de contexte à ce code en expliquant comment cela pourrait résoudre le problème?
MEDZ
1

Vous pouvez utiliser la méthode React.createClone. Créez votre élément, puis créez-en un clone. Lors de la création du clone, vous pouvez injecter des accessoires. Injectez un accessoire onClick: method comme celui-ci

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

la méthode changeColor définira l'état avec le duplicata, vous permettant de définir la couleur dans le processus.

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
la source
Le clonage est totalement inutile.
Emile Bergeron le
-17

Il s'agit d'un modèle React non standard (mais pas si rare) qui n'utilise pas JSX, mais qui met tout en ligne. De plus, c'est Coffeescript.

Le 'React-way' pour faire cela serait avec le propre état du composant:

( 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

Cet exemple fonctionne - je l'ai testé localement. Vous pouvez consulter cet exemple de code exactement sur mon github . À l'origine, l'environnement n'était local que pour mes propres besoins de recherche et de développement sur le tableau blanc, mais je l'ai posté sur Github pour cela. Il peut être réécrit à un moment donné, mais vous pouvez consulter le commit du 8 septembre 2016 pour le voir.

Plus généralement, si vous voulez voir comment fonctionne ce modèle CS / no-JSX pour React, consultez quelques travaux récents ici . Il est possible que j'aie le temps de mettre pleinement en œuvre un POC pour cette idée d'application, dont la pile comprend NodeJS, Primus, Redis et React.

Wylie Kulik
la source
l'arrière-plan n'a pas besoin d'être un dolambda: Cette expression fonctionne aussi:background: if @state.lighted_item is uid then 'magenta' else 'chartreuse'
Wylie Kulik
bonjour comment puis-je voir onclick sur la console du navigateur?
Muneem Habib
12
Pourquoi utiliseriez-vous CoffeeScript dans une réponse à une question qui ne le mentionne en aucune façon? Cela n'a aucun sens et cela peut probablement rendre la réponse plus difficile à lire pour le demandeur, car il pourrait ne pas connaître / aimer CoffeeScript. Downvoter, évidemment.
macbem
7
Non, mais c'est quelque chose qui repose sur le langage, n'est absolument pas standard et nécessite l'installation et la compilation. c'était vraiment un mauvais choix d'écrire votre réponse dans coffeescript quand il n'y a aucun indice du tout qu'ils utilisent coffeescript dans leur projet
TheRealMrCrowley
4
Coffeescript est juste une couche au-dessus de js. FTFY.
machineghost