如何将 react-native props 从功能组件传递到 class 组件
How to pass react-native props from functional component to class component
我正在为流媒体应用创建音乐播放器,我想创建一个动态播放列表。我正在使用 Expo AV 和 Firebase 来存储音乐和信息。
我已经配置好所有内容,但我无法将“cancionesPlaylist”道具(它已经是一个数组)从父功能组件传递到子 class 组件。这是我的代码:
import { StyleSheet, TouchableOpacity, View, Image } 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 cancionesPlaylist = canciones;
return <ReproductorMusica cancionesPlaylist={cancionesPlaylist} />;
}
class ReproductorMusica extends React.Component {
constructor(props) {
super(props);
const cancionesPlaylist = props.cancionesPlaylist;
console.log(cancionesPlaylist);
}
state = {
isPlaying: false,
playbackInstance: null,
currentIndex: 0,
volume: 1.0,
isBuffering: false,
};
async componentDidMount() {
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 loadAudio() {
const { currentIndex, isPlaying, volume } = this.state;
try {
const playbackInstance = new Audio.Sound();
const source = {
uri: cancionesPlaylist[currentIndex].uri,
};
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 < 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 < cancionesPlaylist.length - 1
? (currentIndex += 1)
: (currentIndex = 0);
this.setState({
currentIndex,
});
this.loadAudio();
}
};
renderFileInfo() {
const { playbackInstance, currentIndex } = this.state;
return playbackInstance ? (
<View style={styles.trackInfo}>
<Text style={[styles.trackInfoText, styles.largeText]}>
{cancionesPlaylist[currentIndex].name}
</Text>
<Text style={[styles.trackInfoText, styles.smallText]}>
{cancionesPlaylist[currentIndex].author}
</Text>
<Text style={[styles.trackInfoText, styles.smallText]}>
{cancionesPlaylist[currentIndex].source}
</Text>
</View>
) : null;
}
render() {
return (
<View style={styles.container}>
<Image
style={styles.albumCover}
source={{
uri:
"http://www.archive.org/download/LibrivoxCdCoverArt8/hamlet_1104.jpg",
}}
/>
<View style={styles.controls}>
<TouchableOpacity
style={styles.control}
onPress={this.handlePreviousTrack}
>
<Ionicons name="arrow-back-circle-outline" size={48} color="#444" />
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handlePlayPause}
>
{this.state.isPlaying ? (
<Ionicons name="ios-pause" size={48} color="#444" />
) : (
<Ionicons name="ios-play-circle" size={48} color="#444" />
)}
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handleNextTrack}
>
<Ionicons
name="arrow-forward-circle-outline"
size={48}
color="#444"
/>
</TouchableOpacity>
</View>
{this.renderFileInfo()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#aaa",
alignItems: "center",
justifyContent: "center",
},
albumCover: {
width: 250,
height: 250,
},
trackInfo: {
padding: 40,
backgroundColor: "#aaa",
},
trackInfoText: {
textAlign: "center",
flexWrap: "wrap",
color: "#550088",
},
largeText: {
fontSize: 22,
},
smallText: {
fontSize: 16,
},
control: {
margin: 20,
},
controls: {
flexDirection: "row",
},
});
您几乎掌握了它,只是缺少 class 和功能组件之间的一个概念差异。即组件内部如何使用 props (docs).
对于一个简单的功能组件,简单地说,一个函数,所有的属性都可以在组件中任何地方访问。但是对于 class 组件,道具通过 this.props
.
绑定到 class instance
因此,现在查看您的代码,您正在 class 构造函数内部创建一个局部变量 cancionesPlaylist
,但该变量的范围仅限于构造函数 block。
constructor(props) {
super(props);
const cancionesPlaylist = props.cancionesPlaylist;
console.log(cancionesPlaylist);
}
因此,当您尝试在 class 中的任何位置访问该局部变量时,您实际上是在引用一个可能不存在的(即 undefined
)名为 cancionesPlaylist
的全局变量。您需要做的只是通过 this.props.cancionesPlaylist
访问 class 实例上的道具。您可以访问 this
(aka the component/class instance) anywhere inside the class component, but it may not always be the same see this 以了解一些并非特定于反应的一般问题。
加上一点清理...
import { StyleSheet, TouchableOpacity, View, Image } 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 cancionesPlaylist = canciones;
return <ReproductorMusica cancionesPlaylist={cancionesPlaylist} />;
}
class ReproductorMusica extends React.Component {
state = {
isPlaying: false,
playbackInstance: null,
currentIndex: 0,
volume: 1.0,
isBuffering: false,
};
async componentDidMount() {
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 loadAudio() {
const { currentIndex, isPlaying, volume } = this.state;
try {
const playbackInstance = new Audio.Sound();
const source = {
uri: this.props.cancionesPlaylist[currentIndex].uri,
};
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 } = this.state;
return playbackInstance ? (
<View style={styles.trackInfo}>
<Text style={[styles.trackInfoText, styles.largeText]}>
{this.props.cancionesPlaylist[currentIndex].name}
</Text>
<Text style={[styles.trackInfoText, styles.smallText]}>
{this.props.cancionesPlaylist[currentIndex].author}
</Text>
<Text style={[styles.trackInfoText, styles.smallText]}>
{this.props.cancionesPlaylist[currentIndex].source}
</Text>
</View>
) : null;
}
render() {
return (
<View style={styles.container}>
<Image
style={styles.albumCover}
source={{
uri:
"http://www.archive.org/download/LibrivoxCdCoverArt8/hamlet_1104.jpg",
}}
/>
<View style={styles.controls}>
<TouchableOpacity
style={styles.control}
onPress={this.handlePreviousTrack}
>
<Ionicons name="arrow-back-circle-outline" size={48} color="#444" />
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handlePlayPause}
>
{this.state.isPlaying ? (
<Ionicons name="ios-pause" size={48} color="#444" />
) : (
<Ionicons name="ios-play-circle" size={48} color="#444" />
)}
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handleNextTrack}
>
<Ionicons
name="arrow-forward-circle-outline"
size={48}
color="#444"
/>
</TouchableOpacity>
</View>
{this.renderFileInfo()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#aaa",
alignItems: "center",
justifyContent: "center",
},
albumCover: {
width: 250,
height: 250,
},
trackInfo: {
padding: 40,
backgroundColor: "#aaa",
},
trackInfoText: {
textAlign: "center",
flexWrap: "wrap",
color: "#550088",
},
largeText: {
fontSize: 22,
},
smallText: {
fontSize: 16,
},
control: {
margin: 20,
},
controls: {
flexDirection: "row",
},
});
Removed the constructor
as it was not needed and changed global variable lookup of cancionesPlaylist
to prop lookup on class via this.props.cancionesPlaylist
.
我正在为流媒体应用创建音乐播放器,我想创建一个动态播放列表。我正在使用 Expo AV 和 Firebase 来存储音乐和信息。
我已经配置好所有内容,但我无法将“cancionesPlaylist”道具(它已经是一个数组)从父功能组件传递到子 class 组件。这是我的代码:
import { StyleSheet, TouchableOpacity, View, Image } 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 cancionesPlaylist = canciones;
return <ReproductorMusica cancionesPlaylist={cancionesPlaylist} />;
}
class ReproductorMusica extends React.Component {
constructor(props) {
super(props);
const cancionesPlaylist = props.cancionesPlaylist;
console.log(cancionesPlaylist);
}
state = {
isPlaying: false,
playbackInstance: null,
currentIndex: 0,
volume: 1.0,
isBuffering: false,
};
async componentDidMount() {
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 loadAudio() {
const { currentIndex, isPlaying, volume } = this.state;
try {
const playbackInstance = new Audio.Sound();
const source = {
uri: cancionesPlaylist[currentIndex].uri,
};
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 < 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 < cancionesPlaylist.length - 1
? (currentIndex += 1)
: (currentIndex = 0);
this.setState({
currentIndex,
});
this.loadAudio();
}
};
renderFileInfo() {
const { playbackInstance, currentIndex } = this.state;
return playbackInstance ? (
<View style={styles.trackInfo}>
<Text style={[styles.trackInfoText, styles.largeText]}>
{cancionesPlaylist[currentIndex].name}
</Text>
<Text style={[styles.trackInfoText, styles.smallText]}>
{cancionesPlaylist[currentIndex].author}
</Text>
<Text style={[styles.trackInfoText, styles.smallText]}>
{cancionesPlaylist[currentIndex].source}
</Text>
</View>
) : null;
}
render() {
return (
<View style={styles.container}>
<Image
style={styles.albumCover}
source={{
uri:
"http://www.archive.org/download/LibrivoxCdCoverArt8/hamlet_1104.jpg",
}}
/>
<View style={styles.controls}>
<TouchableOpacity
style={styles.control}
onPress={this.handlePreviousTrack}
>
<Ionicons name="arrow-back-circle-outline" size={48} color="#444" />
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handlePlayPause}
>
{this.state.isPlaying ? (
<Ionicons name="ios-pause" size={48} color="#444" />
) : (
<Ionicons name="ios-play-circle" size={48} color="#444" />
)}
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handleNextTrack}
>
<Ionicons
name="arrow-forward-circle-outline"
size={48}
color="#444"
/>
</TouchableOpacity>
</View>
{this.renderFileInfo()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#aaa",
alignItems: "center",
justifyContent: "center",
},
albumCover: {
width: 250,
height: 250,
},
trackInfo: {
padding: 40,
backgroundColor: "#aaa",
},
trackInfoText: {
textAlign: "center",
flexWrap: "wrap",
color: "#550088",
},
largeText: {
fontSize: 22,
},
smallText: {
fontSize: 16,
},
control: {
margin: 20,
},
controls: {
flexDirection: "row",
},
});
您几乎掌握了它,只是缺少 class 和功能组件之间的一个概念差异。即组件内部如何使用 props (docs).
对于一个简单的功能组件,简单地说,一个函数,所有的属性都可以在组件中任何地方访问。但是对于 class 组件,道具通过 this.props
.
因此,现在查看您的代码,您正在 class 构造函数内部创建一个局部变量 cancionesPlaylist
,但该变量的范围仅限于构造函数 block。
constructor(props) {
super(props);
const cancionesPlaylist = props.cancionesPlaylist;
console.log(cancionesPlaylist);
}
因此,当您尝试在 class 中的任何位置访问该局部变量时,您实际上是在引用一个可能不存在的(即 undefined
)名为 cancionesPlaylist
的全局变量。您需要做的只是通过 this.props.cancionesPlaylist
访问 class 实例上的道具。您可以访问 this
(aka the component/class instance) anywhere inside the class component, but it may not always be the same see this 以了解一些并非特定于反应的一般问题。
加上一点清理...
import { StyleSheet, TouchableOpacity, View, Image } 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 cancionesPlaylist = canciones;
return <ReproductorMusica cancionesPlaylist={cancionesPlaylist} />;
}
class ReproductorMusica extends React.Component {
state = {
isPlaying: false,
playbackInstance: null,
currentIndex: 0,
volume: 1.0,
isBuffering: false,
};
async componentDidMount() {
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 loadAudio() {
const { currentIndex, isPlaying, volume } = this.state;
try {
const playbackInstance = new Audio.Sound();
const source = {
uri: this.props.cancionesPlaylist[currentIndex].uri,
};
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 } = this.state;
return playbackInstance ? (
<View style={styles.trackInfo}>
<Text style={[styles.trackInfoText, styles.largeText]}>
{this.props.cancionesPlaylist[currentIndex].name}
</Text>
<Text style={[styles.trackInfoText, styles.smallText]}>
{this.props.cancionesPlaylist[currentIndex].author}
</Text>
<Text style={[styles.trackInfoText, styles.smallText]}>
{this.props.cancionesPlaylist[currentIndex].source}
</Text>
</View>
) : null;
}
render() {
return (
<View style={styles.container}>
<Image
style={styles.albumCover}
source={{
uri:
"http://www.archive.org/download/LibrivoxCdCoverArt8/hamlet_1104.jpg",
}}
/>
<View style={styles.controls}>
<TouchableOpacity
style={styles.control}
onPress={this.handlePreviousTrack}
>
<Ionicons name="arrow-back-circle-outline" size={48} color="#444" />
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handlePlayPause}
>
{this.state.isPlaying ? (
<Ionicons name="ios-pause" size={48} color="#444" />
) : (
<Ionicons name="ios-play-circle" size={48} color="#444" />
)}
</TouchableOpacity>
<TouchableOpacity
style={styles.control}
onPress={this.handleNextTrack}
>
<Ionicons
name="arrow-forward-circle-outline"
size={48}
color="#444"
/>
</TouchableOpacity>
</View>
{this.renderFileInfo()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#aaa",
alignItems: "center",
justifyContent: "center",
},
albumCover: {
width: 250,
height: 250,
},
trackInfo: {
padding: 40,
backgroundColor: "#aaa",
},
trackInfoText: {
textAlign: "center",
flexWrap: "wrap",
color: "#550088",
},
largeText: {
fontSize: 22,
},
smallText: {
fontSize: 16,
},
control: {
margin: 20,
},
controls: {
flexDirection: "row",
},
});
Removed the
constructor
as it was not needed and changed global variable lookup ofcancionesPlaylist
to prop lookup on class viathis.props.cancionesPlaylist
.