Как связать сопоставленную функцию onkeypress с аудиоэлементом с помощью React Hooks? - PullRequest
0 голосов
/ 07 апреля 2020

Я реорганизую ранее созданное приложение React на основе классов, чтобы оно работало с использованием хуков. Это простое приложение для драм-машины. Я использую .map, чтобы заполнить страницу массивом объектов, содержащих данные для охвата ударной панели и связанного с ней звука.

{soundBank.map((sound) => (
        <DrumPad
          key={sound.id}
          id={sound.id}
          letter={sound.letter}
          src={sound.src}
          handleDisplay={handleDisplay}
        />
      ))}

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

handleKeyPress = event => {
if (event.keyCode === this.props.letter.charCodeAt()) {
  this.audio.play();
  this.audio.currentTime = 0;
  this.props.handleDisplay(this.props.id);
}

render() {
return (
  <div
    className="drum-pad"
    id={this.props.id}
    onClick={this.handleClick.bind(this)}
    onKeyPress={this.handleKeyPress.bind(this)}
  >
    <h1>{this.props.letter}</h1>
    <h2>{this.props.id}</h2>
    <audio
      className="clip"
      id={this.props.letter}
      src={this.props.src}
      ref={ref => (this.audio = ref)}
    ></audio>
  </div>
);}

Вот как он выглядит как функциональный компонент:

let audio = useRef(null);

const handleKeyPress = (event) => {
if (event.keyCode === props.letter.charCodeAt()) {
  audio.play();
  audio.currentTime = 0;
  props.handleDisplay(props.id);
}
};

return (
<div
  className="drum-pad"
  id={props.id}
  onClick={handleClick}
  onKeyPress={handleKeyPress}
>
  <h1>{props.letter}</h1>
  <h2>{props.id}</h2>
  <audio
    className="clip"
    id={props.letter}
    src={props.src}
    ref={(ref) => (audio = ref)}
  ></audio>
</div>);

Я пытался использовать useCallback, а также создание состояния для аудио, но я не могу заставить его работать. Когда страница загружена, первое нажатие клавиши воспроизводит звук, но любое нажатие после этого, и я получаю ошибку:

Uncaught TypeError: Cannot read property 'play' of null
at HTMLDocument.handleKeyPress

При проверке ошибок с помощью console.log кажется, что всякий раз, когда нажата клавиша, функция handleKeyPress вызывается 9 раз (по одному разу для каждой ударной панели). Как сделать так, чтобы при нажатии одной клавиши активировалась только одна указанная c ударная панель, и как я могу убедиться, что звук не возвращается в ноль? Эта проблема также может быть связана с моим использованием реф. Я делаю это, чтобы выучить хуки (и реагировать в целом), поэтому любые указатели в правильном направлении будут высоко оценены.

РЕШЕНИЕ

Вот мое решение основываясь на выбранном ответе Александра Ковпашко:

const audio = useRef(null);

const handleKeyPress = useCallback((event) => {
if (event.keyCode === props.letter.charCodeAt()) {
  audio.current.play();
  audio.current.currentTime = 0;
  props.handleDisplay(props.id);
}}, []);

<audio
    className="clip"
    id={props.letter}
    src={props.src}
    ref={audio}
  ></audio>

Ответы [ 3 ]

1 голос
/ 07 апреля 2020

Похоже, вы неправильно используете ссылки.

Хук useRef возвращает объект "ref", который вы должны передать непосредственно в ref метку тега <audio>. Затем получите доступ к аудио, используя функцию audio.current in handleKeyPress. Кроме того, вы должны обернуть обработчик событий в useCallback ловушку, потому что текущая реализация дает вам новую handleKeyPress функцию в каждом цикле рендеринга.

PS: Почему вы пытаетесь удалить прослушиватель события в возвращаемом значение из вашей handleKeyPress функции? Нет необходимости управлять прослушивателями событий вручную с помощью React, если вы не добавили их вручную с помощью addEventListener.

0 голосов
/ 07 апреля 2020

Я вижу 3 проблемы:

Когда вы создаете объект ref с помощью useRef, вы можете получить к нему доступ, добавив .current (например: audio.current.play ())

Функция charCodeAt требует индекса, возможно, именно поэтому handleKeyPress вызывается несколько раз

Не вызывайте removeEventListener, если вы не вызывали addEventListener до

0 голосов
/ 07 апреля 2020

Вы можете использовать DOM напрямую вместо ссылок -


let audio = document.getElementById(`${props.letter}`)

...