Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
390 views
in Technique[技术] by (71.8m points)

react native - Component only mounting once in tab screen?

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>
        )
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:

  1. on mount, setup lisetner with onSnapshot
  2. whenever it triggers it means that data has changed, so just get the data you need and update it in local state (since this is where you keep it)
  3. when component unmounts, unsubscribe from listener

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...