Home > database >  Remove an item from FlatList render wrong items
Remove an item from FlatList render wrong items

Time:06-08

I know that it has been asked several times, but in all the other threads the problem is that the author is manipulating the state directly - which I don't (hopefully).

I've got an array of posts that I get from the DB.

I want to be able to filter this array according to the tags each post has. In order to do so, I'm filtering the array, saving the result in a temp array, and then setting another array that holds the current posts to display again using useState. The filter works properly.

The list of posts is rendered in a FlatList

<FlatList
    data={filteredPosts}
    extraData={refreshFlat}
    style={globalStyles.feed}
    renderItem={({ item }) => (
      <Post
        post={item}
        navigation={navigation}
        style={globalStyles.list_of_posts}
      />
    )}
    refreshing={refreshing}
    onRefresh={handleRefresh}
    ListEmptyComponent={() => {
      return (
        <View>
          <Text style={globalStyles.be_first}>
            נראה שאין מה להציג כרגע..
          </Text>
        </View>
      );
    }}
    ItemSeparatorComponent={() => {
      return <View style={{ height: 12 }}></View>;
    }}
    keyExtractor={(item, index) => index.toString()}
    ListHeaderComponent={getHeader}
  />

But when I re-render the list, the wrong items are shown. For example, if the list contains only one post after the filter, the FlatList will display only the first item of the original list.

Just to make clear, the item is the right item, I used console.log both outside and inside the Post component to validate it.

postsList - Holds the original list

filteredPosts - Holds the current posts that I want to display

refreshFlat - I tried to force it to refresh using extraData

The complete component:

import { Text, Pressable, FlatList, View, Modal } from "react-native";
import { globalStyles } from "../../styles/global";
import React, { useState, useEffect } from "react";
import Post from "../../API/Post";
import { useData } from "../../AuthProvider/UserDataProvider";

const FeedScreen = ({ navigation, route }) => {
  const [refreshing, setRefreshing] = useState(true);
  const { getPosts, tagsList, getTags } = useData();
  const [postsList, setPostsList] = useState([]);
  const [modalVisible, setModalVisible] = useState(false);
  const [selectedTags, setSelectedTags] = useState([]);
  const [filteredPosts, setFilteredPosts] = useState([]);
  const [refreshFlat, setRefreshFlat] = useState(false);

  const handleRefresh = () => {
    getPosts()
      .then((posts) => {
        setPostsList(posts);
        setFilteredPosts(posts);
        setSelectedTags([]);
        setRefreshing(false);
      })
      .catch(console.error);
  };

  const handleSelectTag = (tag) => {
    if (selectedTags.includes(tag)) {
      const temp = selectedTags.filter((currTag) => currTag !== tag);
      setSelectedTags(temp);
    } else {
      setSelectedTags((prev) => [...prev, tag]);
    }
  };

  const filterPosts = (tags) => {
    if (tags.length === 0) return setFilteredPosts([...postsList]);
    const temp = postsList.filter((post) =>
      post.data.tags.some((t) => tags.includes(t))
    );
    console.log(temp);
    setFilteredPosts(temp);
    setRefreshFlat((prev) => !prev);
  };

  const getHeader = () => {
    return (
      <View>
        <Modal
          visible={modalVisible}
          animationType="slide"
          onRequestClose={() => {
            setModalVisible((prev) => !prev);
          }}
        >
          <View>
            <FlatList
              data={tagsList}
              renderItem={({ item }) => (
                <Pressable
                  style={{
                    backgroundColor: selectedTags.includes(item.name)
                      ? "green"
                      : "#EAE7E6",
                    padding: 5,
                    margin: 5,
                  }}
                  onPress={() => handleSelectTag(item.name)}
                >
                  <Text>{item.name}</Text>
                </Pressable>
              )}
              numColumns={3}
              ListEmptyComponent={() => {
                return (
                  <View>
                    <Text style={globalStyles.be_first}>
                      נראה שאין מה להציג כרגע..
                    </Text>
                  </View>
                );
              }}
              ItemSeparatorComponent={() => {
                return <View style={{ height: 12 }}></View>;
              }}
              keyExtractor={(item, index) => index.toString()}
            />
          </View>
          <Pressable
            onPress={() => {
              filterPosts(selectedTags);
              setModalVisible(false);
            }}
            style={{ marginLeft: 10, width: 50, height: 50 }}
          >
            <Text>{"סנן"}</Text>
          </Pressable>
        </Modal>
        <Pressable
          onPress={() => setModalVisible(true)}
          style={{ width: "100%", height: 50 }}
        >
          <Text>{"open modal"}</Text>
        </Pressable>
      </View>
    );
  };
  useEffect(() => {
    getPosts()
      .then((posts) => {
        setPostsList(posts);
        setFilteredPosts(posts);
        setRefreshing(false);
      })
      .catch(console.error);
    getTags();
    return;
  }, []);

  return (
    <View style={{ flex: 1 }}>
      <FlatList
        data={filteredPosts}
        extraData={refreshFlat}
        style={globalStyles.feed}
        renderItem={({ item }) => (
          <Post
            post={item}
            navigation={navigation}
            style={globalStyles.list_of_posts}
          />
        )}
        refreshing={refreshing}
        onRefresh={handleRefresh}
        ListEmptyComponent={() => {
          return (
            <View>
              <Text style={globalStyles.be_first}>
                נראה שאין מה להציג כרגע..
              </Text>
            </View>
          );
        }}
        ItemSeparatorComponent={() => {
          return <View style={{ height: 12 }}></View>;
        }}
        keyExtractor={(item, index) => index.toString()}
        ListHeaderComponent={getHeader}
      />
      <Pressable
        title="edit"
        onPress={() => {
          navigation.navigate("CreateProject");
        }}
        style={globalStyles.plus_btn}
      >
        <Text style={globalStyles.plus_btn_text}> </Text>
      </Pressable>
    </View>
  );
};

export default FeedScreen;

CodePudding user response:

Maybe it's the key extractor issue? You are using the index of the array as the key and it could confuse the flatlist when you update the data.

  • Related