Dlaczego mój onClick jest wywoływany podczas renderowania? - React.js

106

Mam komponent, który stworzyłem:

class Create extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    var playlistDOM = this.renderPlaylists(this.props.playlists);
    return (
      <div>
        {playlistDOM}
      </div>
    )
  }

  activatePlaylist(playlistId) {
    debugger;
  }

  renderPlaylists(playlists) {
    return playlists.map(playlist => {
      return <div key={playlist.playlist_id} onClick={this.activatePlaylist(playlist.playlist_id)}>{playlist.playlist_name}</div>
    });
  }
}

function mapStateToProps(state) {
  return {
    playlists: state.playlists
  }
}

export default connect(mapStateToProps)(Create);

Kiedy ja renderta strona, activatePlaylistjest wezwana dla każdego playlistw moim map. Jeśli mi się bind activatePlaylistpodoba:

activatePlaylist.bind(this, playlist.playlist_id)

Mogę też skorzystać z anonimowej funkcji:

onClick={() => this.activatePlaylist(playlist.playlist_id)}

wtedy działa zgodnie z oczekiwaniami. Dlaczego to się dzieje?

jhamm
źródło

Odpowiedzi:

199

Musisz przekazać onClick odwołanie do funkcji, kiedy to robisz activatePlaylist( .. ), wywołujesz funkcję i przekazujesz onClickwartość, która zwróciła activatePlaylist. Możesz skorzystać z jednej z trzech opcji:

1 . za pomocą.bind

activatePlaylist.bind(this, playlist.playlist_id)

2 . za pomocą funkcji strzałek

onClick={ () => this.activatePlaylist(playlist.playlist_id) }

3 . lub funkcja powrotu zactivatePlaylist

activatePlaylist(playlistId) {
  return function () {
     // you code 
  }
}
Oleksandr T.
źródło
Nie pamiętam, żeby działał w poprzednich wersjach w Reactten sposób. Czy źle pamiętam, czy to się apizmieniło?
jhamm
@jhamm Używasz klas ES6 iw tym przypadku powinieneś ręcznie powiązać kontekst.
Oleksandr T.
@AlexanderT. jest jedna rzecz, której nie rozumiem. Jeśli powiążesz kontekst z .bind, jak powiedziałeś w kroku 1, konieczne jest wykonanie kroku 2? a jeśli tak, dlaczego? Ponieważ myślę, że kiedy używasz funkcji strzałkowej, kontekstem jest ten, w którym funkcja została zdefiniowana, ale jeśli dołączamy kontekst za pomocą .bind, kontekst jest już dołączony, prawda?
Rafa Romero
2
@Rafa Romero to tylko trzy różne opcje, możesz użyć jednej z nich
Oleksandr T.
2
Na oficjalnej stronie React Docs znajduje się teraz sekcja, w której udzielono pełnej odpowiedzi na to pytanie: Reactjs.org/docs/faq-functions.html
zenoh
2

To zachowanie zostało udokumentowane, gdy React ogłosił wydanie komponentów opartych na klasach.

https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html

Autobinding

React.createClass ma wbudowaną magiczną funkcję, która automatycznie wiąże wszystkie metody z tym. Może to być trochę mylące dla programistów JavaScript, którzy nie są przyzwyczajeni do tej funkcji w innych klasach, lub może być mylące, gdy przechodzą z React do innych klas.

Dlatego zdecydowaliśmy się nie mieć tego wbudowanego w model klasowy Reacta. Jeśli chcesz, nadal możesz jawnie wstępnie wiązać metody w swoim konstruktorze.

David L. Walsh
źródło
2

Wiem, że ten post ma już kilka lat, ale tylko w celu odniesienia się do najnowszego samouczka / dokumentacji Reacta na temat tego częstego błędu (ja też to zrobiłem) z https://reactjs.org/tutorial/tutorial.html :

Uwaga

Aby zaoszczędzić pisanie i uniknąć mylącego zachowania, użyjemy składni funkcji strzałek dla programów obsługi zdarzeń tutaj i dalej poniżej:

class Square extends React.Component {
 render() {
   return (
     <button className="square" onClick={() => alert('click')}>
       {this.props.value}
     </button>
   );
 }
}

Zwróć uwagę, jak w przypadku onClick = {() => alert ('click')} przekazujemy funkcję jako właściwość onClick. React wywoła tę funkcję dopiero po kliknięciu. Zapominanie () => i pisanie onClick = {alert ('click')} jest częstym błędem i powoduje uruchomienie ostrzeżenia za każdym razem, gdy komponent jest ponownie renderowany.

CAM_344
źródło
1

Sposób, w jaki przekazujesz metodę this.activatePlaylist(playlist.playlist_id), spowoduje natychmiastowe wywołanie metody. Powinieneś przekazać odwołanie do metody do onClickzdarzenia. Postępuj zgodnie z jedną z poniższych implementacji, aby rozwiązać problem.

1.
onClick={this.activatePlaylist.bind(this,playlist.playlist_id)}

Tutaj właściwość bind jest używana do tworzenia odwołania do this.activatePlaylistmetody przez przekazanie thiskontekstu i argumentuplaylist.playlist_id

2.
onClick={ (event) => { this.activatePlaylist.(playlist.playlist_id)}}

Spowoduje to dołączenie funkcji do zdarzenia onClick, które zostanie wywołane tylko po kliknięciu przez użytkownika. Po wykonaniu tego kodu this.activatePlaylistmetoda zostanie wywołana.

Shrishail Uttagi
źródło