Reputation: 319
I have a 5 tab react native application. The fifth tab is the profile screen. Here is the profile tab code, I removed a lot of the stuff because this focuses on component did mount:
class Profile extends React.Component {
constructor(props) {
super(props)
this.state = {
user: this.props.user,
bio: "",
storage_image_uri: '',
postCount: 0,
followerCount: 0,
followingCount: 0,
isLoading: true,
navigation: this.props.navigation,
userUID: Firebase.auth().currentUser.uid,
}
this.firestoreRef =
Firebase.firestore()
.collection('posts')
.doc(this.state.userUID)
.collection('posts')
.orderBy("date_created", "desc");
}
componentDidMount() {
console.log("querying the db again")
this.pullUserInfo()
this.unsubscribe = this.firestoreRef.onSnapshot(this.getCollection);
}
componentWillUnmount(){
this.unsubscribe();
}
pullUserInfo = async() => {
await Firebase.firestore()
.collection('users')
.doc(this.state.userUID)
.get()
.then(function(doc){
if (doc.exists) {
this.setState({
postCount: doc.data().postCount,
followerCount: doc.data().followerCount,
followingCount: doc.data().followingCount,
storage_image_uri: doc.data().profilePic,
bio: doc.data().bio,
isLoading: false
})
} else {
console.log("No such document!");
}
}.bind(this))
}
gotToSettings() {
this.state.navigation.navigate('Settings')
}
renderListHeader = () => {
... deleted, just rendering this information
}
render() {
const { navigation } = this.props;
const renderItem = ({ item }) => (
<CurrentUserPostCell
..deleted, unnecessary for this question
/>
);
if(this.state.isLoading){
return(
<View styles = {styles.container}>
<ActivityIndicator size="large" color="#9E9E9E"/>
</View>
)
}
return (
<View>
<FlatList
data={this.state.userPostsArray}
renderItem={renderItem}
keyExtractor={item => item.key}
ListHeaderComponent={this.renderListHeader}
contentContainerStyle={{ paddingBottom: 50 }}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
/>
</View>
)
}
}
However, when I create a new post, follow a new user, or even change the bio from the settings page, the component does not query the db again. I added console.log("querying the db again") in the componentDidMount(), but it only prints when I click on the profile tab for the first time, and never again.
I have tried componentDidUpdate() but that doesn't fix anything either. How can I fix the issue of the component only mounting the first time I click on the tab, and never again?
EDIT: added settings page on request
class Settings extends React.Component {
constructor(props) {
super(props)
this.state = {
user: this.props.user,
oldBio: "",
newBio: "",
profilePic: "",
isLoading: false,
}
}
componentDidMount() {
this.pullBio()
}
//Pull bio from the db, save it to state
pullBio = async() => {
await Firebase.firestore()
.collection('users')
.doc(Firebase.auth().currentUser.uid)
.get()
.then(function(doc) {
if (doc.exists) {
this.setState ({
oldBio: doc.data().bio
})
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}.bind(this));
}
changeBio = async() => {
this.setState({ isLoading: true })
// This should take us to the right place, adding a temp uid where we need it
await Firebase.firestore()
.collection('users')
.doc(Firebase.auth().currentUser.uid)
.set({
bio: this.state.newBio
}, { merge: true })
.then(() => this.setState ({
oldBio: this.state.newBio,
isLoading: false
}))
.catch(function(error) {
console.error("Error storing and retrieving image url: ", error);
});
}
render() {
if(this.state.isLoading){
return(
<View styles = {styles.container}>
<ActivityIndicator size="large" color="#9E9E9E"/>
</View>
)
}
return (
<View style={styles.container}>
<TouchableOpacity
onPress={this.logOut}>
<Text>Sign out 🤷{this.state.user.username}</Text>
</TouchableOpacity>
<TextInput
style={styles.inputBox}
value={this.state.newBio}
onChangeText={newBio => this.setState({ newBio })}
placeholder={this.state.oldBio}
autoCapitalize='none'
/>
<TouchableOpacity onPress={() => { this.changeBio() }}>
<Text>Change Bio</Text>
</TouchableOpacity>
</View>
)
}
}
Upvotes: 1
Views: 1338
Reputation: 4739
I will give you a higher level answer which should help you solve your problem in a proper way
How can I fix the issue of the component only mounting the first time I click on the tab, and never again?
It is not an issue. Mounting means that component didn't previously exist in dom tree in this place and then it started existing. Your component mounts only once because it starts existing as soon as you open Profile tab for the first time (or when you start the app, depending on TabNavigator setup). You don't want it to remount after that at all. Remounting component means it will be get recreated, losing state, scroll position, input focus, fetched data, everything, every time it remounts
What should you do?
In a simple app without Firebase, you would get the data once (when component mounts), then every time you expect this data to change, you re-fetch it. User updated their info? Refetch. User pressed "refresh"? Refetch. User clicked "profile" tab? Refetch. 2 minutes passed and something might have changed? Refetch. You, as a developer, decide when it is the appropriate time to refetch your data, and refetch it, instead of relying on componentDidMount or any other lifecycle methods. Again, your component is not supposed to remount to update your data, you have to decide when to update it yourself
With Firebase, everything is much easier, because you don't even have to manually decide when it's time to update. onSnapshot
gives you a listener, and Firebase itself will call it whenever user data changes. So all you need to do is:
onSnapshot
What can I do to make componentDidMount run whenever I click on the profile tab, which will query the new data from firebase Firestore and update the information on the profile tab
Likewise, this will also be redundant because that listener will make sure that you have the latest data at all times, and you will not need to update it when clicking on profile tab. But even if Firebase wasn't able to create listeners for you, you don't need to trigger remount on component every time you press on profile icon just to update data, because you could just update data every time you press on icon without remounting
Upvotes: 1
Reputation: 697
When you navigate from screen Setting to screen Profile, Profile is mounted, then componentDidMount of Profile is called.
After that, when you navigate to other screen, Profile still on navigation stack screen, then Profile's componentDidMount will never invoke again
So, the problem is the screen inside tabBar is not re-mount.
You can add this in Tab Config:
unmountOnBlur: true
Link docs: here
Other tricky workaround:
Add event listener to check if Profile screen is focused or not. If Profile is focused, pull your data again.
Link: here
Upvotes: 2
Reputation: 105
Firestore's get
retrieves data once. You might want to try the onSnapshot
method which will listen for any changes in your document.
firebase.firestore.collection('users').doc(userId)
.onSnapshot(function(doc){
//...
})
This should hopefully work, even without compononentDidUpdate since you are already running a state update in componentDidMount.
Read more here.
Upvotes: 1