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.