React JS onClick obsługi zdarzeń

120

mam

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

Chcę pokolorować tło klikniętego elementu listy. Jak mogę to zrobić w React?

Coś jak

$('li').on('click', function(){
    $(this).css({'background-color': '#ccc'});
});
user544079
źródło

Odpowiedzi:

95

Dlaczego nie:

onItemClick: function (event) {

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

},

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

A jeśli chcesz bardziej reagować na to, możesz ustawić wybrany element jako stan, w którym zawiera składnik React, a następnie odwołać się do tego stanu, aby określić kolor elementu w 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>
    );
},

Którą chcesz umieścić te <li>s do pętli, i trzeba zrobić li.oni li.offstyle ustawić swój background-color.

ericsoco
źródło
Ręczna manipulacja DOM w Reakcie jest anty-wzorcem, który prowadzi tylko do większej liczby problemów. Unikaj rzeczy takich jak event.currentTarget.style.backgroundColor = '#ccc';chyba, że ​​naprawdę rozumiesz, co robisz (w większości przypadków podczas integracji widżetów innych firm).
Emile Bergeron
61

Myślę o dwóch sposobach

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

To mój ulubiony.

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

Oto DEMO

Mam nadzieję, że to pomoże.

Dhiraj
źródło
8
Nie zaleca się stosowania wiązania w funkcji renderowania, ponieważ będzie to robić za każdym razem, gdy komponent jest renderowany. możesz przenieść to do jakiejś funkcji, która działa na początku cyklu życia
jony89
1
@ jony89 zgodził się, jeśli .bindnie przyjmuje dodatkowego parametru. Ale w pierwszym przypadku tak. Myślę, że nie ma innego sposobu
Dhiraj
1
Jest, stwórz trzy różne funkcje (które powstały w wyniku getComponent.bind (this, 1)), chociaż ta określona może być decyzją (zrobiłaby to dla 2-3 komponentów, a nie dla 20 - chyba że jest to naprawdę problem z wydajnością i łatwe do dynamicznego tworzenia).
jony89
38

Oto jak definiujesz procedurę obsługi zdarzeniareakcji onClick , która odpowiadała na tytuł pytania ... używając składni 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
źródło
9
W metodach nie bindnależy używać funkcji ani funkcji strzałkowych, renderponieważ powodują one za każdym razem tworzenie nowej funkcji. Skutkuje to zmianą stanu komponentu, a komponenty o zmienionym stanie są zawsze renderowane ponownie. Dla singla ato nic wielkiego. W przypadku generowanych list z klikalnymi elementami staje się to bardzo szybko. Dlatego jest specjalnie ostrzegany przed.
hippietrail
18

Użyj ECMA2015. Funkcje strzałek sprawiają, że „to” jest dużo bardziej intuicyjne.

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
źródło
2
indexnic tu nie robi?
północnoamerykańska
@northamerican - Nie, to tylko po to, aby dodać trochę przejrzystości parametrów
itcropper
5
W rzeczywistości jest to niekorzystne dla wydajności, ponieważ tworzy nową funkcję przy każdym renderowaniu. Zobacz: stackoverflow.com/questions/36677733/…
Jochie Nabuurs
1
I proszę, nie używaj jQuery w React, jeśli nie musisz!
Emile Bergeron
13

Jeśli używasz ES6, oto prosty przykładowy kod:

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;

W treściach klas ES6 funkcje nie wymagają już słowa kluczowego „function” i nie muszą być oddzielane przecinkami. Jeśli chcesz, możesz również użyć składni =>.

Oto przykład z dynamicznie tworzonymi elementami:

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;

Należy pamiętać, że każdy dynamicznie utworzony element powinien mieć unikalny „klucz” odniesienia.

Ponadto, jeśli chcesz przekazać rzeczywisty obiekt danych (zamiast zdarzenia) do swojej funkcji onClick, będziesz musiał przekazać to do swojego bind. Na przykład:

Nowa funkcja onClick:

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

Przekazywanie obiektu danych:

{this.state.data.map(d => {
    return(
      <li key={d.id} onClick={this.getComponent.bind(this, d)}>{d.name}</li>
    )}
)}
dirtigr00vz
źródło
Próbuję zbudować dynamicznie moje elementy li, a następnie wydaje się, że staje się to niezdefiniowane, a funkcja onClick zgłasza błąd.
wylądował
1
Właśnie znalazłem podobną odpowiedź, w której musisz użyć .bind (this)); na końcu funkcji anonimowej, ponieważ tutaj odnosi się do okna, dopóki nie utworzysz powiązania ...
wylądował
6

Obsługa zdarzeń za pomocą elementów React jest bardzo podobna do obsługi zdarzeń w elementach DOM. Istnieją pewne różnice składniowe:

  • Zdarzenia React są nazywane przy użyciu camelCase zamiast małych liter.
  • W JSX przekazujesz funkcję jako procedurę obsługi zdarzenia, a nie ciąg.

Tak więc, jak wspomniano w dokumentacji Reacta , są one dość podobne do normalnego HTML, jeśli chodzi o obsługę zdarzeń, ale nazwy zdarzeń w Reakcie przy użyciu camelcase, ponieważ nie są tak naprawdę HTML, są JavaScript, również przekazujesz funkcję, gdy przekazujemy wywołanie funkcji w formacie string dla HTML są różne, ale koncepcje są dość podobne ...

Spójrz na poniższy przykład, zwróć uwagę na sposób przekazywania zdarzenia do funkcji:

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

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}
Alireza
źródło
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
źródło
Wydaje się to zasadniczo identyczne z 11-punktową odpowiedzią i wskrzesza ładne - lub pytanie - dlaczego?
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 Mam nadzieję, że to demo może pomóc :) Polecam zmianę koloru tła poprzez przełączenie nazwy klasy.

dabeng
źródło
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
źródło
czy możesz podać więcej kontekstu do tego kodu, wyjaśniając, jak to może rozwiązać problem?
MEDZ
1

Możesz skorzystać z metody React.createClone. Stwórz swój element, niż stwórz jego klon. Podczas tworzenia klona możesz wstrzykiwać rekwizyty. Wstaw atrybut metody onClick: w ten sposób

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

metoda changeColor ustawi stan z duplikatem, umożliwiając ustawienie koloru w procesie.

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.
źródło
Klonowanie jest całkowicie niepotrzebne.
Emile Bergeron
-17

Jest to niestandardowy (ale nie tak rzadki) wzorzec React, który nie używa JSX, zamiast tego umieszcza wszystko w linii. Poza tym to Coffeescript.

Aby to zrobić, należy użyć własnego stanu komponentu:

( 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

Ten przykład działa - przetestowałem go lokalnie. Możesz sprawdzić ten przykładowy kod dokładnie na moim githubie . Początkowo środowisko env było lokalne tylko dla moich własnych celów badawczo-rozwojowych, ale w tym celu opublikowałem je na Github. W pewnym momencie może zostać napisane, ale możesz sprawdzić zatwierdzenie z 8 września 2016, aby to zobaczyć.

Mówiąc bardziej ogólnie, jeśli chcesz zobaczyć, jak działa ten wzorzec CS / no-JSX dla Reacta, zapoznaj się z ostatnimi pracami tutaj . Możliwe, że będę miał czas na pełne wdrożenie POC dla tego pomysłu na aplikację, w skład którego wchodzą NodeJS, Primus, Redis i React.

Wylie Kulik
źródło
tło nie musi być dolambdą: To wyrażenie działa również:background: if @state.lighted_item is uid then 'magenta' else 'chartreuse'
Wylie Kulik,
cześć, jak mogę wyświetlić onclick w konsoli przeglądarki?
Muneem Habib
12
Dlaczego miałbyś używać CoffeeScript w odpowiedzi na pytanie, które w żaden sposób go nie wspomina? Nie ma to żadnego sensu i prawdopodobnie może utrudnić odczytanie odpowiedzi pytającemu, ponieważ może nie znać / lubić CoffeeScript. Oczywiście głosowanie przeciw.
Macbem
7
Nie, ale jest to coś zbudowanego na bazie języka, absolutnie nie jest standardowe i wymaga instalacji i kompilacji. to był naprawdę kiepski wybór, aby napisać swoją odpowiedź w skrypcie coffeescript, gdy w swoim projekcie ZERO nie sugeruje, że używają skryptu coffeescript
TheRealMrCrowley
4
Coffeescript to tylko warstwa na wierzchu js. FTFY.
machineghost