Por que meu onClick está sendo chamado na renderização? - React.js

100

Tenho um componente que criei:

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 eu renderesta página, activatePlaylisté chamado para cada um playlistno meu map. Se eu bind activatePlaylistgosto:

activatePlaylist.bind(this, playlist.playlist_id)

Também posso usar uma função anônima:

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

então ele funciona conforme o esperado. Por que isso acontece?

Jhamm
fonte

Respostas:

191

Você precisa passar para onClick referência a função, ao fazer assim activatePlaylist( .. )você chama a função e passa para o onClickvalor que retornou activatePlaylist. Você pode usar uma destas três opções:

1 . usando.bind

activatePlaylist.bind(this, playlist.playlist_id)

2 . usando a função de seta

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

3 . ou função de retorno deactivatePlaylist

activatePlaylist(playlistId) {
  return function () {
     // you code 
  }
}
Alexander T.
fonte
Não me lembro de ter funcionado em versões anteriores Reactdessa forma. Estou me lembrando errado ou apimudou?
jhamm
@jhamm Você está usando classes ES6 e, neste caso, deve vincular o contexto manualmente.
Alexander T.
@AlexanderT. tem uma coisa que eu não entendo. Se você vincular o contexto com .bind, como você disse na etapa 1, é necessário executar a etapa 2? e se for, por quê? Porque eu acho que quando você usa a função seta, o contexto é aquele que é onde a função foi definida, mas se a gente estiver anexando o contexto usando .bind, o contexto já está anexado, certo?
Rafa Romero
2
@Rafa Romero são apenas três opções diferentes, você pode usar uma delas
Alexander T.
2
Agora há uma seção no site oficial do React Docs que respondeu a essa pergunta em profundidade: reactjs.org/docs/faq-functions.html
zenoh
2

Este comportamento foi documentado quando o React anunciou o lançamento de componentes baseados em classes.

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

Autobinding

React.createClass tem um recurso mágico integrado que vincula todos os métodos a isso automaticamente para você. Isso pode ser um pouco confuso para desenvolvedores de JavaScript que não estão acostumados com esse recurso em outras classes, ou pode ser confuso quando mudam de React para outras classes.

Portanto, decidimos não ter isso embutido no modelo de classe do React. Você ainda pode pré-vincular explicitamente os métodos em seu construtor, se desejar.

David L. Walsh
fonte
2

Eu sei que esta postagem já tem alguns anos, mas apenas para consultar o tutorial / documentação mais recente do React sobre esse erro comum (eu também cometi) em https://reactjs.org/tutorial/tutorial.html :

Nota

Para economizar digitação e evitar o comportamento confuso disso, usaremos a sintaxe da função de seta para manipuladores de eventos aqui e mais abaixo:

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

Observe como com onClick = {() => alert ('click')}, estamos passando uma função como o prop onClick. O React só chamará esta função após um clique. Esquecer () => e escrever onClick = {alert ('click')} é um erro comum e dispararia o alerta toda vez que o componente fosse renderizado novamente.

CAM_344
fonte
1

A maneira como você passa o método this.activatePlaylist(playlist.playlist_id)chama o método imediatamente. Você deve passar a referência do método para o onClickevento. Siga uma das implementações mencionadas abaixo para resolver seu problema.

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

Aqui, a propriedade bind é usada para criar uma referência do this.activatePlaylistmétodo passando o thiscontexto e o argumentoplaylist.playlist_id

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

Isso anexará uma função ao evento onClick, que será acionado apenas na ação de clique do usuário. Quando este código for executado, o this.activatePlaylistmétodo será chamado.

Shrishail Uttagi
fonte