The Problem was that the Audio.Sound
component sent playbackStatus updates to the callback registered with sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate)
in the time BETWEEN the cleanup function was called and unloadAsync() returned unsynchronously. And that callback updated other state components which where already unmounted.
Solution
Add a boolean variable to the component (not a useState variable because that will not update immediately) which is set in the cleanup function and acts as a guard for the playbackStatus callback.
const [audio] = useState(new Audio.Sound());
let isUnmounting = false;
useEffect(() => {
loadAudio(url).then(() => consoloe.log("loaded audio"));
return () => {
isUnmounting = true;
audio.unloadAsync().then(() => console.log("unloaded audio"));
};
}, [audio]);
async function loadAudio(url) {
// ...
audio.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate);
try {
await audio.loadAsync(url);
// ...
} catch {
console.log("Error loading audio");
}
}
const onPlaybackStatusUpdate = (status) => {
// this protects against updates on unmounted components
// when callback is fired while component is dismounting
if (isUnmounting) return;
// ... set the state components you need, like durationInMillis,..
}