Expo-AV Stop Audio from Component in Parent Screen

SDK Version: 35 / Platforms(Android/iOS/web/all): all

I am building a radio station app that has multiple stations. The audio plays great, but when a user goes back to the home screen, i want to stop the audio so they don’t overlay. The problem is I am calling the play/pause button in a component, so the station page doesn’t have access to the function to stop the audio. How can a parent page stop audio from it’s child component?

import React from "react";
import { TouchableOpacity } from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { Audio } from "expo-av";
import styled from "styled-components";

const audioBookPlaylist = [
  {
    uri: "http://stream.falconinternet.net:8020/;mp3"
  }
];

export default class App extends React.Component {
  state = {
    isPlaying: false,
    playbackInstance: null,
    currentIndex: 0,
    volume: 1.0,
    isBuffering: true
  };

  async componentDidMount() {
    try {
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: false,
        interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
        playsInSilentModeIOS: true,
        interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
        shouldDuckAndroid: true,
        staysActiveInBackground: true,
        playThroughEarpieceAndroid: false
      });

      this.loadAudio();
    } catch (e) {
      console.log(e);
    }
  }

  async loadAudio() {
    const { currentIndex, isPlaying, volume } = this.state;

    try {
      const playbackInstance = new Audio.Sound();
      const source = {
        uri: audioBookPlaylist[currentIndex].uri
      };

      const status = {
        shouldPlay: isPlaying,
        volume: volume
      };

      playbackInstance.setOnPlaybackStatusUpdate(this.onPlaybackStatusUpdate);
      await playbackInstance.loadAsync(source, status, false);
      this.setState({
        playbackInstance
      });
    } catch (e) {
      console.log(e);
    }
  }

  onPlaybackStatusUpdate = status => {
    this.setState({
      isBuffering: status.isBuffering
    });
  };

  handlePlayPause = async () => {
    const { isPlaying, playbackInstance } = this.state;
    isPlaying
      ? await playbackInstance.pauseAsync()
      : await playbackInstance.playAsync();

    this.setState({
      isPlaying: !isPlaying
    });
  };

  renderFileInfo() {
    const { playbackInstance, currentIndex } = this.state;
    return null;
  }

  render() {
    return (
      <PlayButtonContainer>
        <TouchableOpacity onPress={this.handlePlayPause}>
          <Buttons>
            {this.state.isPlaying ? (
              <Ionicons name="ios-pause" size={64} color="#fff" />
            ) : (
              <Ionicons name="ios-play-circle" size={64} color="#fff" />
            )}
          </Buttons>
        </TouchableOpacity>
      </PlayButtonContainer>
    );
  }
}

const PlayButtonContainer = styled.View`
  width: 90;
  height: 100;
  background-color: #224c87;
  text-align: center;
`;

const Buttons = styled.Text`
  width: 64px;
  height: 64px;
  top: 15px;
  left: 13px;
  text-align: center;
`;

I added this, which stops the audio upon exit, but it’s giving a warning:

async componentWillUnmount() {
    const { playbackInstance } = this.state;
    await playbackInstance.pauseAsync();
  }

Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, the componentWillUnmount method,

1 Like

Above code works.

I’m having the same issue and seeing the same warning. Were you able to fix it?

Never mind. My issue was with another component. Once I removed that component, the sound worked as expected.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.