Skip to content
Advertisement

React Native: Why is Expo-AV not playing audio?

I’m attempting to build an app that acts as a soundboard and can play different sounds when different buttons are pressed. I expected the audio to play but instead, I got an error that stated:

Possible Unhandled Promise Rejection (id: 1): TypeError: undefined is not an object (evaluating '_expoAv.Audio.sound.createAsync')
playSound$

This is my code:

import { StatusBar } from "expo-status-bar";
import React, { useState, useEffect } from "react";
import { StyleSheet, Text, View, TouchableOpacity } from "react-native";
import { Feather } from "@expo/vector-icons";
import { Audio } from "expo-av";

// project made in yarn
// yarn start
// yarn add

export default function App() {
  const [sound, setSound] = useState("");

  const playSound = async () => {
    console.log("Loading Sound");
    const { sound } = await Audio.sound.createAsync(
      require("./assets/Ayo.mp3")
    );
    setSound(sound);

    console.log("playing sound");
    await sound.playAsync();
  };

  useEffect(() => {
    return sound
      ? () => {
          console.log("unloading the sound");
          sound.unloadAsync();
        }
      : undefined;
  }, [sound]);
  // run useEffect whenever sound state changes

  return (
    <View style={styles.container}>
      <Text>SoundGround</Text>

      <View style={{ flexDirection: "row" }}>
        <TouchableOpacity onPress={playSound}>
          <Feather name="arrow-left" style={styles.iconStyle} />
        </TouchableOpacity>

        <TouchableOpacity onPress={playSound}>
          <Feather name="arrow-right" style={styles.iconStyle} />
        </TouchableOpacity>
      </View>

      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
  iconStyle: {
    fontSize: 100,
  },
});

So far, I’ve tried adding a try/catch block, using different audio formats. I think the error has to do with state not being assigned before it is used to play something – but I’m not sure.

Advertisement

Answer

You can implement useRef instead of useState to stop re-rendering!

This worked for me very well.

import { StatusBar } from "expo-status-bar";
import React, { useEffect } from "react";
import { StyleSheet, Text, View, TouchableOpacity } from "react-native";
import { Feather } from "@expo/vector-icons";
import { Audio } from "expo-av";

// project made in yarn
// yarn start
// yarn add

export default function App() {
  const sound = useRef(new Audio.Sound());

  useEffect(() => {
    return () => sound.current.unloadAsync();
  }, []);

  const playSound = async () => {
    console.log("Loading Sound");

    await sound.current.createAsync(require("./assets/Ayo.mp3"));

    console.log("playing sound");

    const checkLoaded = await sound.current.getStatusAsync();
    if (checkLoaded.isLoaded === true) {
      console.log("Error in Loading mp3");
    } else {
      await sound.current.playAsync();
    }
  };

  return (
    <View style={styles.container}>
      <Text>SoundGround</Text>

      <View style={{ flexDirection: "row" }}>
        <TouchableOpacity onPress={() => playSound()}>
          <Feather name="arrow-left" style={styles.iconStyle} />
        </TouchableOpacity>

        <TouchableOpacity onPress={() => playSound()}>
          <Feather name="arrow-right" style={styles.iconStyle} />
        </TouchableOpacity>
      </View>

      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
  iconStyle: {
    fontSize: 100,
  },
});
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement