Manipulador de eventos onClick React JS

120

eu tenho

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

Quero colorir o fundo do elemento da lista clicado. Como posso fazer isso no React?

Algo como

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

Respostas:

95

Por que não:

onItemClick: function (event) {

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

},

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

E se você quiser ser mais reativo sobre isso, pode definir o item selecionado como o estado do componente React que o contém e, em seguida, fazer referência a esse estado para determinar a cor do item em 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>
    );
},

Você deseja colocar esses <li>s em um loop e precisa definir os estilos li.one li.offcomo seu background-color.

Ericsoco
fonte
A manipulação manual do DOM no React é um anti-padrão que só leva a mais problemas. Evite coisas assim, a event.currentTarget.style.backgroundColor = '#ccc';menos que você realmente entenda o que está fazendo (na maioria das vezes, ao integrar widgets de terceiros).
Emile Bergeron
61

Duas maneiras que posso pensar são

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 é meu favorito pessoal.

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

Aqui está uma DEMO

Eu espero que isso ajude.

Dhiraj
fonte
8
Não é recomendado aplicar o vínculo na função de renderização, pois isso fará isso sempre que o componente for renderizado. você pode movê-lo para alguma função que é executada no início do ciclo de vida
jony89
1
@ jony89 concordou se .bindnão usar um parâmetro extra. Mas no primeiro caso, sim. Não acho que haja outra maneira
Dhiraj
1
Existe, crie três funções diferentes (que são criadas pelo resultado de getComponent.bind (this, 1)), embora essa definição possa ser uma decisão (faria isso para 2-3 componentes, não para 20 - a menos que seja realmente um problema de desempenho e fácil de criá-lo dinamicamente).
jony89
38

Aqui está como você define um manipulador de eventos react onClick , que estava respondendo ao título da pergunta ... usando a sintaxe 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
fonte
9
Nem bindas funções de seta nem devem ser usadas nos rendermétodos porque elas resultam na criação de uma nova função a cada vez. Isso tem o efeito de alterar o estado do componente, e os componentes com estado alterado são sempre renderizados novamente. Para um único, aisso não é grande coisa. Para listas geradas com itens clicáveis, isso se torna um grande negócio muito rapidamente. É por isso que é especificamente advertido.
hippietrail
18

Use ECMA2015. As funções das setas tornam "isto" muito mais 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'));`
itcropper
fonte
2
indexnão faz nada aqui?
norte
@northamerican - Não, está lá apenas para adicionar alguma clareza de parâmetro
itcropper
5
Na verdade, isso é ruim para o desempenho, pois cria uma nova função a cada renderização. Consulte: stackoverflow.com/questions/36677733/…
Jochie Nabuurs
1
E, por favor, não use o jQuery dentro do React se não for necessário!
Emile Bergeron
13

Se você estiver usando ES6, aqui está um código de exemplo simples:

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;

Em corpos de classe ES6, as funções não requerem mais a palavra-chave 'function' e não precisam ser separadas por vírgulas. Você também pode usar a sintaxe => se desejar.

Aqui está um exemplo com elementos criados dinamicamente:

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;

Observe que cada elemento criado dinamicamente deve ter uma 'chave' de referência exclusiva.

Além disso, se desejar passar o objeto de dados real (em vez do evento) para a função onClick, você precisará passá-lo para o bind. Por exemplo:

Nova função onClick:

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

Passando o objeto de dados:

{this.state.data.map(d => {
    return(
      <li key={d.id} onClick={this.getComponent.bind(this, d)}>{d.name}</li>
    )}
)}
dirtigr00vz
fonte
Estou tentando construir meus itens li dinâmicos e, em seguida, torna-se indefinido e a função onClick, portanto, gera um erro.
desembarcou em
1
Acabei de encontrar uma resposta semelhante onde você precisa usar .bind (this)); no final da função anônima, pois aqui se refere à janela até que você faça a ligação ...
desembarcou em
6

O tratamento de eventos com elementos React é muito semelhante ao tratamento de eventos em elementos DOM. Existem algumas diferenças sintáticas:

  • Os eventos React são nomeados usando camelCase, em vez de minúsculas.
  • Com JSX, você passa uma função como o manipulador de eventos, em vez de uma string.

Como mencionado na documentação do React , eles são bastante semelhantes ao HTML normal quando se trata de manipulação de eventos, mas os nomes dos eventos no React usando camelcase, porque eles não são realmente HTML, são JavaScript, também, você passa a função enquanto passamos a chamada de função em formato de string para HTML, eles são diferentes, mas os conceitos são muito semelhantes ...

Veja o exemplo abaixo, preste atenção na maneira como o evento é passado para a função:

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

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}
Alireza
fonte
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
fonte
Isso parece essencialmente idêntico à resposta de 11 pontos e ressuscita uma bela - ou pergunta - 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 demonstração possa ajudar :) Eu recomendo alterar a cor do plano de fundo alternando o nome da classe.

dabeng
fonte
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
fonte
você pode fornecer mais contexto para este código explicando como isso pode corrigir o problema?
MEDZ
1

Você pode usar o método React.createClone. Crie seu elemento, em seguida, crie um clone dele. Durante a criação do clone, você pode injetar adereços. Injetar um onClick: método prop como este

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

o método changeColor definirá o estado com a duplicata, permitindo que você defina a cor no processo.

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
fonte
A clonagem é totalmente desnecessária.
Emile Bergeron
-17

Este é um padrão React não padrão (mas não tão incomum) que não usa JSX, em vez disso, coloca tudo embutido. Além disso, é Coffeescript.

O 'React-way' para fazer isso seria com o próprio estado do 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 exemplo funciona - eu testei localmente. Você pode verificar este código de exemplo exatamente no meu github . Originalmente, o env era apenas local para meus propósitos de pesquisa e desenvolvimento de quadro branco, mas eu o postei no Github para isso. Pode ser substituído em algum momento, mas você pode conferir o commit de 8 de setembro de 2016 para ver isso.

De forma mais geral, se você quiser ver como funciona esse padrão CS / no-JSX para React, verifique alguns trabalhos recentes aqui . É possível que eu tenha tempo para implementar totalmente um POC para esta ideia de aplicativo, cuja pilha inclui NodeJS, Primus, Redis e React.

Wylie Kulik
fonte
o fundo não precisa ser um dolambda: Esta expressão também funciona:background: if @state.lighted_item is uid then 'magenta' else 'chartreuse'
Wylie Kulik
Olá, como posso ver onclick no console do navegador?
Muneem Habib
12
Por que você usaria CoffeeScript em uma resposta a uma pergunta que não o menciona de forma alguma? Não faz nenhum sentido e provavelmente pode tornar a resposta mais difícil de ler para quem pergunta, já que ele pode não conhecer / gostar do CoffeeScript. Downvoting, obviamente.
macbem
7
Não, mas é algo construído sobre a linguagem, não é absolutamente padrão e requer instalação e compilação. foi uma escolha realmente ruim escrever sua resposta em coffeescript quando há ZERO insinuação de que eles estão usando coffeescript em seu projeto
TheRealMrCrowley
4
O Coffeescript é apenas uma camada sobre js. FTFY.
machineghost