从流音频功能更新useState时无限重渲染

时间:2019-09-30 15:19:01

标签: javascript reactjs react-native audio react-hooks

我正在构建一个应用程序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 个答案:

答案 0 :(得分:2)

请记住,函数主体中的所有内容都将在每个渲染器上运行-因此,在这种情况下,您将创建一个新的soundObject并可能在每个单个渲染器上运行soundObject.loadAsync调用。您需要利用其他挂钩来避免这种情况-在您的情况下可能是useRefuseEffect。我建议您通过钩子API参考熟悉这些内容: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