javascript - 如何使用 componentWillUnmount()?
问题描述
我是使用 React-Native 的新手,我正在为流媒体应用程序创建音乐播放器,几乎一切正常,它在后台播放,但是当我想切换到另一张专辑或播放列表时,它不会剪切正在播放的歌曲要播放新的,它会同时播放上一首歌曲和新一首歌曲。
它向我显示了这个警告:
警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请取消 componentWillUnmount 方法中的所有订阅和异步任务。
但是我不知道如何取消所有订阅和异步任务。
这是我的代码。
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",
},
}); ```
解决方案
这似乎是一个架构问题。
如果您在ReproductorMusica
并且需要离开该屏幕以转到AlbumScreen
您并没有卸载最后一个实例(您已经丢失了,因为您在 ReproductorMusica 中只有一个参考)。
要解决这个问题,您Audio.Sound
需要“全球性和独特性”。因此,您可以从任何屏幕访问,而且始终是同一个实例。
推荐阅读
- google-api - 如何在 Google Calendar API 中获取 CalendarListEntry 的真正所有者以区分拥有日历和共享日历?
- wpf - 如何绑定到 ContentControl 内容对象的属性
- c++ - 二进制表达式的无效操作数('std::ostream'(又名'basic_ostream
'和'无效') - excel - 比较两个列表框的匹配值
- javascript - Eventlistener 在点击几下后停止工作,控制台中没有错误
- c++ - 每个内核上是否“存在”AVX/AVX2?
- python - 实例作为属性(类型错误位置参数)
- javascript - React-redux:登录后用户信息未存储在redux中
- html - 有没有办法让这个 html 文本适合移动屏幕并自动换行?
- python - 在大文件的内容中获取一行