developer
developer

Reputation: 229

display activated activity indicator on current row with react native

Currently working on an app this gives users lists of news on a page and on each news has a textbox where you can input your comment. So for example, 10 news items will have 10 textboxes.

When a user comment after and hit the submit button, the activity indicator appears for all 10 news items, but I want it to only display on where the comment has been made and also after posting the comment, the comment box should be empty

Function

state = {
  posts: [],
  comment: ""
};

commentPost = item => {
  const api = create({
    baseURL: "patch-to-api-url",
    headers: { Accept: "application/json" }
  });
  const self = this;
  self.setState({ modalLoader: true });
  api
    .post("news/posts/" + `${item.id}` + "/comments", {
      media: "",
      text: this.state.comment
    })
    .then(response => {
      console.log(response);
      self.setState({ modalLoader: false });

      //updating the state
      this.setState(prevState => ({
        posts: prevState.posts.map(el => {
          if (el.id === item.id) {
            return {
              ...el,
              commentsCount: el.commentsCount + 1
            };
          }
          return el;
        })
      }));
    });
};

View

<ScrollView>
  {posts.map((item, i) => {
    return (
      <View key={i} style={styles.user}>
        <Card>
          <ListItem
            titleStyle={{ color: "#36c", fontWeight: "500" }}
            onPress={() =>
              navigation.navigate("PostComments", {
                postID: item.id,
                groupID: item.writer.group.id,
                communityID: item.group.community.id
              })
            }
            titleNumberOfLines={2}
            hideChevron={false}
            chevronColor="#36c"
            roundAvatar
            title={item.headline}
            avatar={{
              uri:
                "https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg"
            }}
          />
          <Text
            style={{
              marginBottom: 10,
              fontSize: 16,
              color: "#000",
              fontFamily: "HelveticaNeue-Light"
            }}
          >
            {item.text}
          </Text>
          <TextInput
            onChangeText={onSetComment}
            label="Write Comment"
            underlineColor="#36a"
            style={{ backgroundColor: "#fff", width: "90%" }}
          />

          <View>
            <Icon
              name="md-send"
              type="ionicon"
              color="#999"
              onPress={() => {
                onCommentPost(item);
              }}
            />

            <View style={styles.loading}>
              <ActivityIndicator animating={modalLoader} size="small" />
            </View>
          </View>
        </Card>
      </View>
    );
  })}
</ScrollView>

Upvotes: 0

Views: 468

Answers (2)

JavanPoirier
JavanPoirier

Reputation: 442

You are sharing the modalLoader state among all iterated posts. A Post should be a single stateful component. Then for the specific component that was updated you need to update only that state.

Upvotes: 0

DoHn
DoHn

Reputation: 673

You don't have enough state to accomplish what you want. Wanting an independent spinner in each post implies that you have to store it's state somewhere.

You should add the modalLoader attribute to each post and not globally. Change your function to look like this:

commentPost = item => {
  const api = create({
    baseURL: "patch-to-api-url",
    headers: { Accept: "application/json" }
  });
  const self = this;
  self.setState({ posts: this.state.posts.map(post => post.id === item.id ? {...post, modalLoader: true } : post));
  api
    .post("news/posts/" + `${item.id}` + "/comments", {
      media: "",
      text: this.state.comment
    })
    .then(response => {
      console.log(response);
      self.setState({ posts: this.state.posts.map(post => post.id === item.id ? {...post, modalLoader: false } : post));

      //updating the state
      this.setState(prevState => ({
        posts: prevState.posts.map(el => {
          if (el.id === item.id) {
            return {
              ...el,
              commentsCount: el.commentsCount + 1
            };
          }
          return el;
        })
      }));
    });
};

And your component to look like this:

<ScrollView>
  {posts.map((item, i) => {
    return (
      <View key={i} style={styles.user}>
        <Card>
          <ListItem
            titleStyle={{ color: "#36c", fontWeight: "500" }}
            onPress={() =>
              navigation.navigate("PostComments", {
                postID: item.id,
                groupID: item.writer.group.id,
                communityID: item.group.community.id
              })
            }
            titleNumberOfLines={2}
            hideChevron={false}
            chevronColor="#36c"
            roundAvatar
            title={item.headline}
            avatar={{
              uri:
                "https://s3.amazonaws.com/uifaces/faces/twitter/brynn/128.jpg"
            }}
          />
          <Text
            style={{
              marginBottom: 10,
              fontSize: 16,
              color: "#000",
              fontFamily: "HelveticaNeue-Light"
            }}
          >
            {item.text}
          </Text>
          <TextInput
            onChangeText={onSetComment}
            label="Write Comment"
            underlineColor="#36a"
            style={{ backgroundColor: "#fff", width: "90%" }}
          />

          <View>
            <Icon
              name="md-send"
              type="ionicon"
              color="#999"
              onPress={() => {
                onCommentPost(item);
              }}
            />

            <View style={styles.loading}>
              <ActivityIndicator animating={item.modalLoader} size="small" />
            </View>
          </View>
        </Card>
      </View>
    );
  })}
</ScrollView>

Upvotes: 1

Related Questions