Re rendering a component with an async function inside

I am new to react native and my JS is a bit rusty. I need to be able to change the value of my collection for the firestore. I have two buttons that will change the value of typeOfPost by setting the state. Component1 can successfully get “this.state.typeOfPost”. However, when I click one of the buttons and update the state my log inside of the async function is not being called. It is only called when the app initially renders. What I find weird is that my log on the top of Component1 will display as expected. Is there any better way of doing this?

class Forum extends Component {

 state = {
    typeOfPost: ' '
 }

 onPressSitter = () => {
    this.setState({
      typeOfPost: 'sitterPosts'
      })
 }


  onPressNeedSitter = () => {
    this.setState({
      typeOfPost: 'needPosts'
      })
 }


 render() {
    return (
      <View style={styles.container}>
        
        <View style={styles.row}>
            <TouchableOpacity
            style={styles.button}
            onPress={this.onPressSitter}
            >
            <Text>I am a sitter</Text>
            </TouchableOpacity>

            <TouchableOpacity
            style={styles.button}
            onPress={this.onPressNeedSitter}
            >
            <Text>Need a sitter</Text>
            </TouchableOpacity>

        </View>

        <View>
          <Component1 typeOfPost = {this.state.typeOfPost}> </Component1>
        </View>

      </View>
    )
  }
}

const Component1 = (props) => {
  console.log("type of post " + props.typeOfPost);
  const [loading, setLoading] = useState(true); // Set loading to true on component mount
  const [data, setData] = useState([]); // Initial empty array of data
  const getData = async () => {
    console.log("type of post inside async " + props.typeOfPost);
    const subscriber = firestore()
    .collection(props.typeOfPost) // need to be able to update this
    .onSnapshot(querySnapshot => {
      const data = [];

      querySnapshot.forEach(documentSnapshot => {
        data.push({
          ...documentSnapshot.data(),
          key: documentSnapshot.id,
        });
      });

      setData(data);
      setLoading(false);
    });

    // Unsubscribe from events when no longer in use
    return () => subscriber();
  }

  useEffect(() => {
    getData();
  }, [])

  if (loading) {
    return <ActivityIndicator />;
  }

  return (
      <FlatList
      data={data}
      ListEmptyComponent={
          <View style={styles.flatListEmpty}>
              <Text style={{ fontWeight: 'bold' }}>No Data</Text>
          </View>
      }
      renderItem={({ item }) => (
          <View>
            <Text>User ID: {item.fullName}</Text>
          </View>
      )}
  />
  )
}


Answer

There is a difference between mount and render. I see no problem with your code except the few remarks I have made. The thing is that when you change typeOfPost, the component is rerendered, but the useEffect is not called again, since you said, it’s just called when it was first mounted:

useEffect(() => {

}, []) // ---> [] says to run only when first mounted

However here, you want it to run whenever typeOfPost changes. So here is how you can do this:

useEffect(() => {
    getData();
  }, [typeofPost])
class Forum extends Component {

 state = {
    typeOfPost: ' '
 }

 onPressSitter = () => {
    this.setState({
      typeOfPost: 'sitterPosts'
      })
 }


  onPressNeedSitter = () => {
    this.setState({
      typeOfPost: 'needPosts'
      })
 }


 render() {
    return (
      <View style={styles.container}>
        
        <View style={styles.row}>
            <TouchableOpacity
            style={styles.button}
            onPress={this.onPressSitter}
            >
            <Text>I am a sitter</Text>
            </TouchableOpacity>

            <TouchableOpacity
            style={styles.button}
            onPress={this.onPressNeedSitter}
            >
            <Text>Need a sitter</Text>
            </TouchableOpacity>

        </View>

        <View>
          <Component1 typeOfPost = {this.state.typeOfPost}> </Component1>
        </View>

      </View>
    )
  }
}

const Component1 = (props) => {
  const { typeOfPost } = props 
  console.log("type of post " + props.typeOfPost);
  const [loading, setLoading] = useState(true); // Set loading to true on component mount
  const [data, setData] = useState([]); // Initial empty array of data
  const getData = () => {
    setLoading(true)
    console.log("type of post inside async " + props.typeOfPost);
    const subscriber = firestore()
    .collection(props.typeOfPost) // need to be able to update this
    .onSnapshot(querySnapshot => {
      const data = [];

      querySnapshot.forEach(documentSnapshot => {
        data.push({
          ...documentSnapshot.data(),
          key: documentSnapshot.id,
        });
      });

      setData(data);
      setLoading(false);
    });

    // Unsubscribe from events when no longer in use
    return () => subscriber();
  }

  useEffect(() => {
    getData();
  }, [typeofPost])

  if (loading) {
    return <ActivityIndicator />;
  }

  return (
      <FlatList
      data={data}
      ListEmptyComponent={
          <View style={styles.flatListEmpty}>
              <Text style={{ fontWeight: 'bold' }}>No Data</Text>
          </View>
      }
      renderItem={({ item }) => (
          <View>
            <Text>User ID: {item.fullName}</Text>
          </View>
      )}
  />
  )
}

Leave a Reply

Your email address will not be published. Required fields are marked *