Mengapa onClick saya dipanggil saat render? - React.js

102

Saya memiliki komponen yang telah saya buat:

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

Ketika saya renderhalaman ini, activatePlaylistdipanggil untuk masing-masing playlistdi saya map. Jika saya bind activatePlaylistsuka:

activatePlaylist.bind(this, playlist.playlist_id)

Saya juga dapat menggunakan fungsi anonim:

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

lalu berfungsi seperti yang diharapkan. Mengapa ini terjadi?

jhamm
sumber

Jawaban:

197

Anda perlu pass to onClick reference to function, ketika Anda melakukan ini, activatePlaylist( .. )Anda memanggil fungsi dan meneruskan ke onClicknilai yang dikembalikan activatePlaylist. Anda dapat menggunakan salah satu dari tiga opsi berikut:

1 . menggunakan.bind

activatePlaylist.bind(this, playlist.playlist_id)

2 . menggunakan fungsi panah

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

3 . atau mengembalikan fungsi dariactivatePlaylist

activatePlaylist(playlistId) {
  return function () {
     // you code 
  }
}
Oleksandr T.
sumber
Saya tidak ingat ini berfungsi di versi sebelumnya Reactdengan cara ini. Apakah saya salah mengingat atau sudah apiberubah?
jhamm
@jhamm Anda menggunakan kelas ES6 dan dalam hal ini Anda harus mengikat konteks secara manual.
Oleksandr T.
@AlexanderT. ada satu hal yang saya tidak mengerti. Jika Anda mengikat konteks dengan .bind, seperti yang Anda katakan di langkah 1, perlu melakukan langkah 2? dan jika ya, mengapa? Karena menurut saya ketika Anda menggunakan fungsi panah, konteksnya adalah di mana fungsi itu didefinisikan, tetapi jika kita melampirkan konteks menggunakan .bind, konteksnya sudah terpasang, bukan?
Rafa Romero
2
@Rafa Romero hanya ada tiga opsi berbeda, Anda dapat menggunakan salah satunya
Oleksandr T.
2
Sekarang ada bagian di situs web resmi React Docs yang menjawab pertanyaan ini secara mendalam: reactjs.org/docs/faq-functions.html
zenoh
2

Perilaku ini didokumentasikan ketika React mengumumkan rilis komponen berbasis kelas.

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

Autobinding

React.createClass memiliki fitur ajaib built-in yang mengikat semua metode secara otomatis untuk Anda. Ini bisa sedikit membingungkan bagi pengembang JavaScript yang tidak terbiasa dengan fitur ini di kelas lain, atau bisa membingungkan ketika mereka berpindah dari React ke kelas lain.

Oleh karena itu kami memutuskan untuk tidak memiliki built-in ke dalam model kelas React. Anda masih dapat melakukan prebind metode secara eksplisit dalam konstruktor Anda jika Anda mau.

David L. Walsh
sumber
2

Saya tahu posting ini sudah berumur beberapa tahun, tetapi hanya untuk referensi tutorial / dokumentasi React terbaru tentang kesalahan umum ini (saya juga membuatnya) dari https://reactjs.org/tutorial/tutorial.html :

Catatan

Untuk menghemat pengetikan dan menghindari perilaku membingungkan ini, kita akan menggunakan sintaks fungsi panah untuk pengendali event di sini dan selanjutnya di bawah:

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

Perhatikan bagaimana dengan onClick = {() => alert ('click')}, kita meneruskan fungsi sebagai onClick prop. React hanya akan memanggil fungsi ini setelah satu klik. Forgetting () => dan menulis onClick = {alert ('click')} adalah kesalahan umum, dan akan mengaktifkan peringatan setiap kali komponen merender ulang.

CAM_344
sumber
1

Cara Anda meneruskan metode this.activatePlaylist(playlist.playlist_id), akan segera memanggil metode tersebut. Anda harus meneruskan referensi metode ke onClickacara tersebut. Ikuti salah satu penerapan yang disebutkan di bawah ini untuk menyelesaikan masalah Anda.

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

Di sini properti bind digunakan untuk membuat referensi this.activatePlaylistmetode dengan meneruskan thiskonteks dan argumenplaylist.playlist_id

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

Ini akan melampirkan fungsi ke acara onClick yang akan dipicu hanya pada tindakan klik pengguna. Ketika kode ini keluar, this.activatePlaylistmetode akan dipanggil.

Shrishail Uttagi
sumber