react-native - 图片上传无法在图片位置显示
问题描述
我正在 react-native expo 上开发一个应用程序,并且我创建了一个用户注册,用户可以在其中上传要在用户个人资料上显示的照片,效果很好,但现在我正在尝试为注册时设置的用户信息,我可以在个人资料上显示用户的所有字段,但也许我缺少更新的内容,当我尝试获取新图像时,它没有被显示,希望如此,其他人可以看到我无法显示和更新这些字段的内容。此外,在我尝试更新用户信息之后,还有一个黄色的早晨错误:
[Unhandled promise rejection: FirebaseError: Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: undefined]
这是 Register.js 代码:
import { StatusBar } from 'expo-status-bar';
import Fire from '../../Fire';
import React from 'react';
import { Image, StyleSheet, Text, View } from 'react-native';
import { TextInput, TouchableOpacity } from 'react-native-gesture-handler';
import { Ionicons } from 'react-native-vector-icons';
import UserPermissions from '../../utilities/UserPermissions';
import * as ImagePicker from 'expo-image-picker';
export default class RegisterScreen extends React.Component {
static navigationOptions = {
headerShown: false
};
state = {
user: {
name: '',
state: '',
city: '',
email: '',
password: '',
confirmPassword: '',
profilePhotoUrl: null
},
errorMessage: null,
};
handlePickProfilePhoto = async () => {
UserPermissions.getMediaGalleryPermission();
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1]
});
if (!result.cancelled) {
this.setState({ user: { ...this.state.user, profilePhotoUrl: result.uri } })
};
};
handleSignUp = () => {
Fire.shared.createUser(this.state.user);
};
render() {
if (!this.state.user.password === this.state.user.confirmPassword) {
alert("Passwords don't match");
return;
} else if (this.state.user.name === '' || this.state.user.state === '' ||
this.state.user.city === '' || this.state.user.email === '' ||
this.state.user.password === '' || this.state.user.confirmPassword === '' ) {
alert("All fields must be filled up");
return;
};
return (
<View style={styles.container}>
<StatusBar barStyle='light-content' />
<View
style={{ top: 5, alignItems: 'center', width: '100%' }}>
<Text style={styles.greating}>
{'Welcome aboard.\nSign up to get startd'}
</Text>
<TouchableOpacity style={styles.profilePhotoPlaceholder}
onPress={this.handlePickProfilePhoto}>
<Image
source={{ uri: this.state.user.profilePhotoUrl }}
style={styles.profilePhoto}
/>
<Ionicons
name='ios-add-circle-outline'
size={150} color='#fff'
style={{ marginTop: 1, marginLeft: -10 }}
/>
</TouchableOpacity>
</View>
<View style={styles.errorMessage}>
{this.state.errorMessage && <Text style={styles.errorMessage}>
{this.state.errorMessage}
</Text>}
</View>
<View style={styles.form}>
<View style={{ marginTop: 5 }}>
<Text style={styles.inputTitle}>Full Name</Text>
<TextInput
style={styles.input}
autoCapitalize='none'
onChangeText={name => this.setState({ user: {...this.state.user, name} })}
value={this.state.user.name}
/>
</View>
<View style={{ marginTop: 5 }}>
<Text style={styles.inputTitle}>State of residence</Text>
<TextInput
style={styles.input}
autoCapitalize='none'
onChangeText={state => this.setState({ user: {...this.state.user, state} })}
value={this.state.user.state}
/>
</View>
<View style={{ marginTop: 5 }}>
<Text style={styles.inputTitle}>City of residence</Text>
<TextInput
style={styles.input}
autoCapitalize='none'
onChangeText={city => this.setState({ user: {...this.state.user, city} })}
value={this.state.user.city}
/>
</View>
<View style={{ marginTop: 5 }}>
<Text style={styles.inputTitle}>Email Address</Text>
<TextInput
style={styles.input}
autoCapitalize='none'
onChangeText={email =>
this.setState({ user: {...this.state.user, email} })}
value={this.state.user.email}
/>
</View>
<View style={{ marginTop: 5 }}>
<Text style={styles.inputTitle}>Password</Text>
<TextInput
style={styles.input}
autoCapitalize='none'
secureTextEntry={true}
onChangeText={password =>
this.setState({ user: {...this.state.user, password} })}
value={this.state.user.password}
/>
</View>
<View style={{ marginTop: 5 }}>
<Text style={styles.inputTitle}>Confirm Password</Text>
<TextInput
style={styles.input}
autoCapitalize='none'
secureTextEntry={true}
onChangeText={confimrPassword =>
this.setState({ user: {...this.state.user, confimrPassword} })}
value={this.state.user.confimrPassword}
/>
</View>
</View>
<TouchableOpacity style={styles.button} onPress={this.handleSignUp}>
<Text style={{ color: '#fff', fontWeight: '500' }}>Sign up</Text>
</TouchableOpacity>
<TouchableOpacity style={{ alignSelf: 'center', }}>
<Text
style={{ color: '#fff', fontSize: 13 }}
onPress={() => this.props.navigation.navigate('Login')}
>
Already Begrato? <Text style={{ color: '#ff2222', fontWeight: '500' }}>
Login
</Text>
</Text>
</TouchableOpacity>
</View>
);
};
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#263237',
},
greating: {
color: '#ffffffdd',
marginTop: 6,
fontSize: 18,
fontWeight: '400',
textAlign: 'center'
},
errorMessage: {
color: '#ff222266',
height: 72,
alignItems: 'center',
justifyContent: 'center',
marginHorizontal: 30
},
error: {
color: '#e9446a',
fontSize: 13,
fontWeight: '600',
textAlign: 'center',
},
form: {
marginBottom: 48,
marginHorizontal: 30,
},
inputTitle: {
color: '#156cc5',
fontSize: 10,
textTransform: 'uppercase',
},
input: {
borderBottomColor: '#ff2222',
borderBottomWidth: StyleSheet.hairlineWidth,
height: 25,
fontSize: 15,
color: '#7cdcfe'
},
button: {
marginBottom: 25,
marginHorizontal: 30,
backgroundColor: '#e9446a',
borderRadius: 4,
height: 52,
alignItems: 'center',
justifyContent: 'center',
},
back: {
top: -10,
position: 'relative',
left: 10
},
profilePhoto: {
position: 'absolute',
width: 120,
height: 120,
borderRadius: 50,
},
profilePhotoPlaceholder: {
width: 120,
height: 120,
backgroundColor: '#e1e2e6',
borderRadius: 50,
marginTop: 10,
justifyContent: 'center',
alignItems: 'center',
},
});
这是我正在处理的 Profile.js 代码:
import React, { useState, useEffect } from 'react';
import {
Button,
Image,
Text,
View,
FlatList,
StyleSheet,
Modal,
TextInput } from 'react-native';
import styled from 'styled-components';
import * as ImagePicker from 'expo-image-picker';
import UserPermissions from '../../utilities/UserPermissions';
import { Feather } from 'react-native-vector-icons';
import firebase from 'firebase';
require('firebase/firestore');
import { connect } from 'react-redux';
function Profile(props) {
const [userPosts, setUserPosts] = useState([]);
const [user, setUser] = useState(null);
const [open, setOpen] = useState(false);
const [hasMediaLibraryPermission, setHasMediaLibraryPermission] = useState(null);
const [profilePhotoUrl, setProfilePhotoUrl] = useState(user?.profilePhotoUrl);
const [name, setName] = useState(user?.name);
const [state, setState] = useState(user?.state);
const [city, setCity] = useState(user?.city);
const [email, setEmail] = useState(user?.email);
useEffect(() => {
const { currentUser, posts } = props;
(async () => {
const mediaLibraryStatus = await ImagePicker.requestMediaLibraryPermissionsAsync();
setHasMediaLibraryPermission(mediaLibraryStatus.status === 'granted');
})();
if (props.route.params.uid === firebase.auth().currentUser.uid) {
setUser(currentUser)
setUserPosts(posts)
} else {
firebase.firestore()
.collection('users')
.doc(props.route.params.uid)
.get()
.then((snapshot) => {
if (snapshot.exists) {
setUser(snapshot.data());
}
else {
console.log('does not exist')
}
})
firebase.firestore()
.collection('posts')
.doc(props.route.params.uid)
.collection('userPosts')
.orderBy('creation', 'desc')
.get()
.then((snapshot) => {
let posts = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
return { id, ...data }
})
setUserPosts(posts)
});
};
//content deleted
}, [props.route.params.uid]);
// create new upload functions
async function updateProfile(){
if(name === '' || state === '' || city === ''){
return;
}
await firebase.firestore().collection('users')
.doc(user.uid).update({
name: name,
state: state,
city: city,
profilePhotoUrl: profilePhotoUrl
})
//Search for all posts from user
const postDocs = await firebase.firestore().collection('posts')
.where('userId', '==', user.uid ).get();
//Search and update names of the user posts
postDocs.forEach( async doc => {
await firebase.firestore().collection('posts').doc(doc.id).update({
name: name,
state: state,
city: city,
})
});
let data = {
uid: user.uid,
name: name,
state: state,
city: city,
profilePhotoUrl: profilePhotoUrl,
email: user.email,
};
setUser(data);
storageUser(data);
setOpen(false);
};
async function handlePickProfilePhoto() {
UserPermissions.getMediaGalleryPermission();
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1]
});
if (!result.cancelled) {
setProfilePhotoUrl({ user: {...user, profilePhotoUrl: result.uri} });
};
setProfilePhotoUrl(result);
};
// Image upload
const uploadFile = () => {
const options = {
noData: true,
mediaType: 'photo'
};
ImagePicker.launchImageLibrary(options, response => {
if(response.didCancel){
console.log('CANCELOU O MODAL.');
}else if(response.error){
console.log('Parece que deu algum erro: ' + response.error);
}else{
uploadFileFirebase(response);
setUrl(response.uri);
}
})
}
const getFileLocalPath = response => {
const { path, uri } = response;
return Platform.OS === 'android' ? path : uri;
};
const uploadFileFirebase = async response => {
const fileSource = getFileLocalPath(response);
const storageRef = storage().ref('users').child(user?.uid);
return await storageRef.putFile(fileSource)
};
const onLogout = () => {
firebase.auth().signOut();
};
if (user === null) {
return <View />
}
return (
<View style={styles.container}>
<View style={{ alignItems: 'center' }}>
<View style={styles.profilePhotoContainer}>
{
user.profilePhotoUrl ?
(
<UploadButton>
<Text style={styles.uploadText}>+</Text>
<Image style={styles.profilePhoto} source={{ uri: user.profilePhotoUrl }} />
</UploadButton>
) :
(
<Image style={styles.profilePhoto}
source={require('../../assets/defaultProfilePhoto.jpg')}
/>
)
}
</View>
</View>
<View style={styles.containerInfo}>
<Text>Usuário: {user?.name}</Text>
<Text>Estado: {user?.state}</Text>
<Text>Usuário: {user?.city}</Text>
<Text>E-mail: {user?.email}</Text>
<View style={{ alignItems: 'center', marginBottom: 10, }}>
<ButtonStyled bg='#428cfd' onPress={() => setOpen(true)}>
<ButtonText color='#fff'>Update Profile</ButtonText>
</ButtonStyled>
</View>
<Modal visible={open} animationType="slide" transparent={true}>
<View style={styles.modalContainer}>
<View style={{ marginTop: 5, marginBottom: 10 }}>
<Button
title='Pick Image From Gallery'
onPress={() => handlePickProfilePhoto()}
/>
<Button
title='Save'
onPress={uploadFile}
/>
</View>
<ButtonBack onPress={() => setOpen(false)}>
<Feather
name="arrow-left"
size={22}
color="#121212"
/>
<ButtonText color="#121212" >Voltar</ButtonText>
</ButtonBack>
<TextInput
placeholder={user?.name}
value={name}
onChangeText={(text) => setName(text)}
/>
<TextInput
placeholder={user?.state}
value={state}
onChangeText={(text) => setState(text)}
/>
<TextInput
placeholder={user?.city}
value={city}
onChangeText={(text) => setCity(text)}
/>
<TextInput
placeholder={user?.email}
value={email}
onChangeText={(text) => setEmail(text)}
/>
<ButtonStyled bg="#428cfd" onPress={updateProfile}>
<ButtonText color="#f1f1f1">Atualizar</ButtonText>
</ButtonStyled>
</View>
</Modal>
<Button
title='Logout'
onPress={() => onLogout()}
/>
</View>
<View style={styles.containerGallery}>
<FlatList
numColumns={3}
horizontal={false}
data={userPosts}
renderItem={({ item }) => (
<View
style={styles.containerImage}>
<Image
style={styles.image}
source={{ uri: item.downloadURL }} // it comes from posts
/>
</View>
)}
/>
</View>
</View>
);
};
const ButtonStyled = styled.TouchableOpacity`
margin-top: 12px;
align-items: center;
justify-content: center;
background-color: ${props => props.bg};
width: 80%;
height: 45px;
border-radius: 5px;
`;
const ButtonText = styled.Text`
font-size: 20px;
color: ${props => props.color};
font-style: italic;
`;
const ButtonBack = styled.TouchableOpacity`
position: absolute;
top: 18px;
left: 25px;
flex-direction: row;
align-items: center;
`;
const UploadButton = styled.TouchableOpacity`
background-color: #fff;
width: 165px;
height: 165px;
border-radius: 90px;
justify-content: center;
align-items: center;
z-index: 5;
`;
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerInfo: {
margin: 20
},
containerGallery: {
flex: 1
},
containerImage: {
flex: 1 / 3
},
image: {
flex: 1,
aspectRatio: 1 / 1
},
cameraContainer: {
flex: 1,
flexDirection: 'row'
},
fixedRatio: {
flex: 1,
aspectRatio: 1
},
modalContainer: {
width: '100%',
height: '70%',
backgroundColor: '#FFF',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
bottom: 0,
},
input: {
width: '90%',
height: 50,
backgroundColor: '#DDD',
borderRadius: 10,
padding: 10,
fontSize: 20,
textAlign: 'center',
},
profilePhoto: {
width: 160,
height: 160,
borderRadius: 80,
},
profilePhotoContainer: {
alignItems: 'center',
marginTop: '5%',
shadowOpacity: 0.8,
shadowRadius: 30,
shadowColor: '#222'
},
errorMessage: {
color: '#ff222266',
height: 72,
alignItems: 'center',
justifyContent: 'center',
marginHorizontal: 30
},
uploadText: {
zIndex: 1,
position: 'absolute',
fontSize: 55,
color: '#ff8000',
opacity: 0.4
},
})
const mapStateToProps = (store) => ({
currentUser: store.userState.currentUser,
posts: store.userState.posts
})
export default connect(mapStateToProps, null)(Profile);
我不知道我还应该尝试更新这些字段,那里
解决方案
推荐阅读
- json - 是否可以同时使用 Spring Boot 应用程序来控制网站和作为 API?
- windows - Windows Server Web Application Load Balancer - 如何检查它是使用粘性会话还是循环?
- c++ - 使用 void* 正确地将对象传递给 pthread_create()
- javascript - Javascript a = b 语句设置 b 值?
- r - 更改 bs4_book (bookdown) 中的右侧边栏标题?
- node.js - Feathers JS MongoDB 服务:动态集合名称
- amazon-web-services - 多设备和时间戳的最佳分区方法
- angular - 如何从Angular中的多选下拉菜单中汇总多个值
- swift - 关联的枚举表示为自定义字体的字符串和 CGFloat?
- reactjs - 在 React 中使用数据 URI 而不是小于 10kb 的图像的路径的原因是什么?