Perché il mio onClick viene richiamato durante il rendering? - React.js


100

Ho un componente che ho creato:

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

Quando ho renderquesta pagina, activatePlaylistviene chiamato per ciascuno playlistnel mio map. Se mi bind activatePlaylistpiace:

activatePlaylist.bind(this, playlist.playlist_id)

Posso anche usare una funzione anonima:

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

quindi funziona come previsto. Perché succede questo?

Risposte:


191

È necessario passare per fare onClick riferimento alla funzione, quando si esegue questa operazione activatePlaylist( .. )si chiama funzione e si passa al onClickvalore restituito da activatePlaylist. Puoi utilizzare una di queste tre opzioni:

1 . utilizzando.bind

activatePlaylist.bind(this, playlist.playlist_id)

2 . utilizzando la funzione freccia

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

3 . o funzione di ritorno daactivatePlaylist

activatePlaylist(playlistId) {
  return function () {
     // you code 
  }
}

Non ricordo che funzionasse in Reactquesto modo nelle versioni precedenti . Sto ricordando male o è apicambiato?
jhamm

@jhamm Stai usando classi ES6 e in questo caso dovresti associare il contesto manualmente.
Alexander T.

@AlexanderT. c'è una cosa che non capisco. Se leghi il contesto con .bind, come dici al passaggio 1, è necessario fare il passaggio 2? e se lo è, perché? Perché penso che quando usi la funzione freccia, il contesto è quello in cui è stata definita la funzione, ma se stiamo allegando il contesto usando .bind, il contesto è già allegato, giusto?
Rafa Romero

2
@Rafa Romero sono solo tre diverse opzioni, puoi usarne una
Alexander T.

2
Ora c'è una sezione sul sito web ufficiale di React Docs che ha risposto a questa domanda in modo approfondito: reactjs.org/docs/faq-functions.html
zenoh

2

Questo comportamento è stato documentato quando React ha annunciato il rilascio di componenti basati su classi.

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

Autobinding

React.createClass ha una funzione magica incorporata che lega automaticamente tutti i metodi a questo. Questo può creare un po 'di confusione per gli sviluppatori JavaScript che non sono abituati a questa funzionalità in altre classi, oppure può creare confusione quando passano da React ad altre classi.

Pertanto abbiamo deciso di non avere questo integrato nel modello di classe di React. Puoi ancora prebindare esplicitamente i metodi nel tuo costruttore, se lo desideri.


2

So che questo post è già vecchio di alcuni anni, ma solo per fare riferimento all'ultimo tutorial / documentazione di React su questo errore comune (l'ho fatto anche io) da https://reactjs.org/tutorial/tutorial.html :

Nota

Per salvare la digitazione ed evitare il comportamento confuso di questo, useremo la sintassi della funzione freccia per i gestori di eventi qui e più avanti:

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

Nota come con onClick = {() => alert ('click')}, stiamo passando una funzione come prop onClick. React chiamerà questa funzione solo dopo un clic. Dimenticare () => e scrivere onClick = {alert ('click')} è un errore comune e attiva l'avviso ogni volta che il componente viene nuovamente visualizzato.


1

Il modo in cui passi il metodo this.activatePlaylist(playlist.playlist_id), chiamerà il metodo immediatamente. Dovresti passare il riferimento del metodo onClickall'evento. Segui una delle implementazioni indicate di seguito per risolvere il tuo problema.

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

Qui la proprietà bind viene utilizzata per creare un riferimento del this.activatePlaylistmetodo passando thiscontesto e argomentoplaylist.playlist_id

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

Questo collegherà una funzione all'evento onClick che verrà attivato solo all'azione del clic dell'utente. Quando questo codice viene eseguito, il this.activatePlaylistmetodo verrà chiamato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.