[英]How to use componentWillUnmount()?
I am new using React-Native and I'm creating a music player for a streaming app and almost everything works fine, it plays in the background but when I want to switch to another album or playlist it does not cut the song that is playing to play the new one, it plays the previous song and the new one at the same time.我是使用 React-Native 的新手,我正在为流媒体应用程序创建音乐播放器,几乎一切正常,它在后台播放,但是当我想切换到另一张专辑或播放列表时,它不会剪切正在播放的歌曲新的,它同时播放上一首歌曲和新一首歌曲。
It shows me this warning:它向我显示了这个警告:
Warning: Can't perform a React state update on an unmounted component.警告:无法对未安装的组件执行 React state 更新。 This is a no-op, but it indicates a memory leak in your application.
这是一个无操作,但它表明您的应用程序中存在 memory 泄漏。 To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
要修复,请取消 componentWillUnmount 方法中的所有订阅和异步任务。
But I don't know how to cancel all subscriptions and asynchronous tasks.但是我不知道如何取消所有订阅和异步任务。
Here is my code.这是我的代码。
import {
StyleSheet,
TouchableOpacity,
View,
Image,
ImageBackground,
Slider,
} from "react-native";
import { Title, Text } from "react-native-paper";
import { LinearGradient } from "expo-linear-gradient";
import { Button } from "../components/Button";
import { Audio, Video } from "expo-av";
import firebase from "../utils/firebase";
import "firebase/firestore";
import { Ionicons } from "@expo/vector-icons";
export default function ReproductorAudio(props) {
const { route } = props;
const { canciones } = route.params;
const [duration, setDuration] = useState(0);
const [totalDuration, setTotalDuration] = useState(0);
const cancionesPlaylist = canciones;
console.log(cancionesPlaylist);
return (
<ReproductorMusica
duration={duration}
cancionesPlaylist={cancionesPlaylist}
totalDuration={totalDuration}
/>
);
}
class ReproductorMusica extends React.Component {
state = {
isPlaying: false,
playbackInstance: null,
currentIndex: 0,
duration: 0,
volume: 1.0,
isBuffering: false,
isMounted: false,
totalDuration: 0,
};
async componentDidMount() {
this.isMounted = true;
try {
await Audio.setAudioModeAsync({
allowsRecordingIOS: false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DUCK_OTHERS,
shouldDuckAndroid: true,
staysActiveInBackground: true,
playThroughEarpieceAndroid: true,
});
this.loadAudio();
} catch (e) {
console.log(e);
}
}
async componentWillUnmount() {
Audio.setAudioModeAsync({
allowsRecordingIOS: false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DUCK_OTHERS,
shouldDuckAndroid: true,
staysActiveInBackground: true,
playThroughEarpieceAndroid: true,
});
}
async loadAudio() {
const { currentIndex, isPlaying, volume } = this.state;
try {
const playbackInstance = new Audio.Sound();
const source = {
uri: this.props.cancionesPlaylist[currentIndex].song,
};
const status = {
shouldPlay: isPlaying,
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,
});
};
handlePreviousTrack = async () => {
let { playbackInstance, currentIndex } = this.state;
if (playbackInstance) {
await playbackInstance.unloadAsync();
currentIndex < this.props.cancionesPlaylist.length - 1
? (currentIndex -= 1)
: (currentIndex = 0);
this.setState({
currentIndex,
});
this.loadAudio();
}
};
handleNextTrack = async () => {
let { playbackInstance, currentIndex } = this.state;
if (playbackInstance) {
await playbackInstance.unloadAsync();
currentIndex < this.props.cancionesPlaylist.length - 1
? (currentIndex += 1)
: (currentIndex = 0);
this.setState({
currentIndex,
});
this.loadAudio();
}
};
renderFileInfo() {
const {
playbackInstance,
currentIndex,
duration,
totalDuration,
} = this.state;
return playbackInstance ? (
<View style={styles.trackInfo}>
<Image
style={styles.albumCover}
source={{
uri: this.props.cancionesPlaylist[currentIndex].image,
}}
/>
<Title style={[styles.trackInfoText, styles.largeText]}>
{this.props.cancionesPlaylist[currentIndex].name}
</Title>
<Title style={[styles.trackInfoText, styles.smallText]}></Title>
<View style={styles.progressContainer}>
<Slider
totalDuration={this.props.cancionesPlaylist[currentIndex].duracion}
onValueChange={(value) =>
this.props.cancionesPlaylist[duration](value)
}
/>
<View style={styles.durationContainer}>
<Text style={styles.durationTextLeft}>{duration}</Text>
<Text style={styles.durationTextRight}>
-{(totalDuration - duration).toFixed(2)}
</Text>
</View>
</View>
{/*<Title style={[styles.trackInfoText, styles.smallText]}>
{this.props.cancionesPlaylist[currentIndex].pista}
</Title>*/}
</View>
) : null;
}
render() {
const { playbackInstance, currentIndex } = this.state;
return (
<ImageBackground
style={styles.backgroundImage}
source={{
uri: this.props.cancionesPlaylist[currentIndex].image,
}}
blurRadius={25}
>
<View style={styles.container}>
{this.renderFileInfo()}
<View style={styles.controls}>
<TouchableOpacity
style={styles.control}
onPress={this.handlePreviousTrack}
>
<Ionicons
name="arrow-back-circle-outline"
size={48}
color="#fff"
/>
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handlePlayPause}
>
{this.state.isPlaying ? (
<Ionicons name="ios-pause" size={48} color="#fff" />
) : (
<Ionicons name="ios-play-circle" size={48} color="#fff" />
)}
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handleNextTrack}
>
<Ionicons
name="arrow-forward-circle-outline"
size={48}
color="#fff"
/>
</TouchableOpacity>
</View>
</View>
</ImageBackground>
);
}
}
const styles = StyleSheet.create({
backgroundImage: {
position: "absolute",
top: 0,
left: 0,
bottom: 0,
right: 0,
},
container: {
flex: 1,
backgroundColor: "rgba(189,0,0,0.3)",
alignItems: "center",
justifyContent: "center",
},
albumCover: {
width: 250,
height: 250,
borderRadius: 10,
borderWidth: 5,
borderColor: "#fff",
},
trackInfo: {
padding: 40,
paddingBottom: 0,
//backgroundColor: "#000",
},
trackInfoText: {
textAlign: "center",
flexWrap: "wrap",
color: "#fff",
},
largeText: {
fontSize: 22,
},
smallText: {
fontSize: 16,
},
control: {
margin: 20,
color: "#fff",
},
controls: {
flexDirection: "row",
color: "#fff",
},
durationContainer: {
flexDirection: "row",
},
durationTextLeft: {
flex: 0.5,
textAlign: "left",
fontSize: 16,
fontWeight: "bold",
color: "white",
},
durationTextRight: {
flex: 0.5,
textAlign: "right",
fontSize: 16,
fontWeight: "bold",
color: "white",
},
}); ```
This seems to be an architectural problem.这似乎是一个架构问题。
If you are in the ReproductorMusica
and need to leave that screen to go to the AlbumScreen
you're not unloading the last instance (which you've lost, since you only had a reference in the ReproductorMusica).如果您在
ReproductorMusica
中并且需要将该屏幕留给 go 到AlbumScreen
,那么您不会卸载最后一个实例(您已经丢失了,因为您在 ReproductorMusica 中只有一个参考)。
To solve this problem, your Audio.Sound
needs to be "global and unique".要解决这个问题,您的
Audio.Sound
需要是“全局且唯一的”。 So you can access from any screen, and it's always the same instance.因此,您可以从任何屏幕访问,而且始终是同一个实例。
I really encourage you to change your components to function components.You are using both for no reason, and class components are harder to use state management.我真的鼓励您将您的组件更改为 function 组件。您无缘无故地使用这两个组件,并且 class 组件更难使用 state 管理。 You can change to function components and use just "useEffect" hook to do everything you need.
您可以更改为 function 组件并使用“useEffect”挂钩来完成您需要的一切。 Read more about here: https://reactjs.org/docs/hooks-effect.html
在此处阅读更多信息: https://reactjs.org/docs/hooks-effect.html
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.