Почему мой onClick вызывается при рендеринге? - React.js

104

У меня есть компонент, который я создал:

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

Когда я renderоткрываю эту страницу, activatePlaylistвызывается для каждого playlistв моем map. Если мне bind activatePlaylistнравится:

activatePlaylist.bind(this, playlist.playlist_id)

Я также могу использовать анонимную функцию:

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

тогда он работает как положено. Почему так происходит?

джхамм
источник

Ответы:

198

Вам нужно перейти к onClick ссылке на функцию, когда вы делаете это, activatePlaylist( .. )вы вызываете функцию и переходите к onClickзначению, которое было возвращено activatePlaylist. Вы можете использовать один из трех вариантов:

1 . с помощью.bind

activatePlaylist.bind(this, playlist.playlist_id)

2 . используя стрелочную функцию

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

3 . или вернуть функцию изactivatePlaylist

activatePlaylist(playlistId) {
  return function () {
     // you code 
  }
}
Александр Т.
источник
Я не помню, чтобы это работало в предыдущих версиях Reactтаким образом. Я неправильно запомнил или apiизменилось?
jhamm
@jhamm Вы используете классы ES6, и в этом случае вам следует привязать контекст вручную.
Александр Т.
@AlexanderT. есть одна вещь, которую я не понимаю. Если вы привязываете контекст .bind, как вы говорите в шаге 1, необходимо ли делать шаг 2? а если да, то почему? Потому что я думаю, что когда вы используете стрелочную функцию, контекст - это тот, в котором функция была определена, но если мы .bindподключаем контекст, используя , контекст уже прикреплен, верно?
Рафа Ромеро
2
@Rafa Romero, это всего лишь три разных варианта, вы можете использовать один из них
Александр Т.
2
На официальном сайте React Docs появился раздел, в котором на этот вопрос дан подробный ответ
zenoh
2

Такое поведение было задокументировано, когда React объявил о выпуске компонентов на основе классов.

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

Автосвязывание

React.createClass имеет встроенную магическую функцию, которая автоматически привязывает к нему все методы. Это может немного сбить с толку разработчиков JavaScript, которые не привыкли к этой функции в других классах, или может сбить с толку, когда они переходят от React к другим классам.

Поэтому мы решили не включать это в модель классов React. Вы по-прежнему можете явно предварительно привязать методы в своем конструкторе, если хотите.

Дэвид Л. Уолш
источник
2

Я знаю, что этому посту уже несколько лет, но просто чтобы сослаться на последний учебник / документацию по React об этой распространенной ошибке (я тоже ее сделал) из https://reactjs.org/tutorial/tutorial.html :

Заметка

Чтобы сохранить ввод и избежать запутанного поведения, мы будем использовать синтаксис стрелочной функции для обработчиков событий здесь и далее ниже:

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

Обратите внимание, как с onClick = {() => alert ('click')} мы передаем функцию как опору onClick. React вызовет эту функцию только после щелчка. Забыть () => и написать onClick = {alert ('click')} - распространенная ошибка, которая будет вызывать предупреждение каждый раз при повторной визуализации компонента.

CAM_344
источник
1

То, как вы передаете метод this.activatePlaylist(playlist.playlist_id), вызовет метод немедленно. Вы должны передать в onClickсобытие ссылку на метод . Следуйте одной из описанных ниже реализаций, чтобы решить вашу проблему.

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

Здесь свойство bind используется для создания ссылки на this.activatePlaylistметод путем передачи thisконтекста и аргументаplaylist.playlist_id

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

Это добавит функцию к событию onClick, которое будет запускаться только при нажатии пользователем. Когда этот код выполнится, this.activatePlaylistбудет вызван метод.

Шришаил Уттаги
источник