Бесконечный повторный рендеринг при обновлении useState из функции потокового аудио - PullRequest
0 голосов
/ 30 сентября 2019

Я создаю приложение React Native, где я отправляю массив аудиофайлов в объект Expo AV Audio.Sound, загружаю их, воспроизводю их, а затем пытаюсь обновить отображение самого приложения информацией овоспроизводимый аудиофайл (в частности, как далеко находится файл от пользователя). Я пытаюсь обновить отображение через ловушку useState, которая вызывается функцией обратного вызова из аудиоплеера.

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

import React, { useState} from 'react';
import { Audio } from 'expo-av';

const AudioPlayer = ({ user }) => {
    const [currentProgress, setCurrentProgress] = useState(0);

    const soundObject = new Audio.Sound();
    soundObject.setOnPlaybackStatusUpdate(playbackUpdate);
    // sets a function that is called every 500 milliseconds as the audio is played 

    if(user) {
          soundObject.loadAsync({user.message.path});
    }

    const play = () => {
          soundObject.playAsync();
    }

    const playbackUpdate = (playbackObject) => {
          setCurrentProgress(playbackObject.currentMillis);
          // updating state with progress through audio file in milliseconds
    }

    return (
          <View>
             <Text>{currentProgress}</Text>
             <Button title="play" onPress={play} />
          </View>
    )

}

export default AudioPlayer

1 Ответ

1 голос
/ 30 сентября 2019

Помните, что все в вашем теле функции будет выполняться при каждом рендере - поэтому в этом случае вы создаете новый soundObject и потенциально запускаете вызов soundObject.loadAsync для каждого отдельного рендера. Чтобы избежать этого, вам нужно воспользоваться другими хуками - в вашем случае вероятно useRef и useEffect. Я бы порекомендовал ознакомиться с ними через описание API-интерфейса hooks: https://reactjs.org/docs/hooks-reference.html

Вот краткий отчет о том, как избежать ненужных эффектов. Вы, вероятно, захотите просмотреть и настроить массивы зависимостей в зависимости от того, как вы хотите, чтобы вещи функционировали, и когда вы хотите, чтобы различные эффекты были перезапущены. Я не уверен, что вам когда-нибудь понадобится воссоздать, например, объект Sound.

import React, { useState, useRef, useCallback, useEffect} from 'react';
import { Audio } from 'expo-av';

const AudioPlayer = ({ user }) => {
    const [currentProgress, setCurrentProgress] = useState(0);

    const soundObjectRef = useRef(new Audio.Sound());

    useEffect(() => {

      const playbackUpdate = (playbackObject) => {
          setCurrentProgress(playbackObject.currentMillis);
          // updating state with progress through audio file in milliseconds
      }
      soundObjectRef.current.setOnPlaybackStatusUpdate(playbackUpdate);
    }, []); // do this only once per component mount
    // sets a function that is called every 500 milliseconds as the audio is played 

    useEffect(() => {
      if (user) {
        soundObjectRef.current.loadAsync({user.message.path});
      }
    }, [user]); // run this anytime user changes but do not run again if user doesn't change

    const play = () => {
          soundObjectRef.current.playAsync();
    }

    return (
          <View>
             <Text>{currentProgress}</Text>
             <Button title="play" onPress={play} />
          </View>
    )

}

export default AudioPlayer

...