Karty kategorii React Native - każda karta renderuje inną kategorię
Obecnie próbuję zaimplementować ekran przeglądania, na którym użytkownicy mogą wybrać określoną kategorię z ScrollView, który renderuje posty związane z tą kategorią.
Mój interfejs API pobiera wszystkie dane postów z zaplecza i dla każdego postu mam wywołanie parametru categoryId.
To, co próbuję zrobić, to na przykład jeśli jedną z moich kategorii jest sport. kiedy użytkownik wybierze zakładkę sport, powinna zostać wyświetlona płaska lista zawierająca wszystkie posty o sporcie.
Dzięki tej obecnej implementacji mam karty kategorii i kiedy wybieram którąkolwiek z zakładek, renderowane są wszystkie posty, a nie posty z tej konkretnej kategorii.
Oto mój kod:
function SearchScreen({ navigation }) {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const loadPosts = async () => {
setLoading(true);
const response = await postsApi.getPosts(page);
setLoading(false);
if (!response.ok) return setError(true);
setError(false);
setPosts(response.data);
};
const[page,setPage]=useState(0);
useEffect(() => {
loadPosts();
}, []);
const categories = [
{
label: "Sports",
id: 1,
},
{
label: "Fashion",
id: 2,
},
{
label: "News",
id: 3,
},
];
const[label,setLabel]=useState('Sports')
const[dataList,setDataList]=useState(posts)
const setLabelFilter=label=>{
setLabel(label)
}
return (
<>
<ActivityIndicator visible={loading} />
<Screen style={styles.screen}>
{error && (
<>
<View style={styles.error}>
<AppText>Could not retrieve posts from server.</AppText>
<AppButton title="Retry" onPress={loadPosts} />
</View>
</>
)}
<ScrollView
horizontal
style={{flexDirection:"row",
alignContent:"center",
width:Dimensions.get('window').width/1.05,
marginBottom:20,
}}
showsHorizontalScrollIndicator={false}
>
{categories.map(e=>(
<TouchableOpacity
style={[
{paddingHorizontal:10,paddingBottom:10},
label===e.label &&
{borderBottomWidth:3,
borderBottomColor:colors.primary,
borderRadius:2,
}
]}
onPress={()=>setLabelFilter(e.label)}
>
<AppText style={[{fontWeight:"500",color:colors.medium},label===e.label &&
{color:colors.primary,fontWeight:"700"}]}>{e.label}</AppText>
</TouchableOpacity>
))}
</ScrollView>
<FlatList
data={posts} // to have all the data
keyExtractor={(post) => post.id.toString()}
renderItem={({ item,index }) => (
<Card
title={item.title}
subTitle={item.subTitle}
onPress={() => navigation.navigate(routes.POST_DETAILS, {post:item,index})}
/>
)}
/>
</Screen>
</>
);
}
Kiedy robię console.log (posty), tak wygląda moja odpowiedź:
Object {
"Category": Object {
"id": 1,
"name": "Sports",
},
"categoryId": 3,
"createdAt": "2020-11-18T13:43:19.616Z",
"deletedAt": null,
"id": 506,
"subtTitle": "test",
"title": "test",
"updatedAt": "2020-11-23T06:57:44.150Z",
"userId": 1,
},
Myślałem o zmianie mojego wywołania API, aby uzyskać kategorię według jej identyfikatora, a następnie uwzględnić wszystkie posty dla każdej określonej kategorii, ale to nie zadziałało, więc poszedłem z wywołaniem API, aby uzyskać wszystkie posty.
Aktualizacja
const[label,setLabel]=useState('Sports')
const setLabelFilter=label=>{
setLabel(label)
}
const [currentCategoryId, setCurrentCategoryId] = useState()
const toggleBrands = (categoryId) => {
setCurrentCategoryId(categoryId)
setLabel(label)
};
return(
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
>
{categories.map(e=>(
<TouchableOpacity
key={e.id}
onPress={()=>{toggleBrands(e.id),
setLabelFilter(e.label)}}
selected={e.id === currentCategoryId}
>
<AppText>{e.label}</AppText>
</TouchableOpacity>
))}
</ScrollView>
<FlatList
data={currentCategoryId ?
posts.filter(post=>post.categoryId===currentCategoryId
):posts}
Wypróbowałem następujący kod, czy ktoś może mi powiedzieć, czy jest poprawny, czy nie?
Odpowiedzi
Aby dać ci przykład, powiedzmy, że najpierw klikniesz element na płaskiej liście (pokazującej wszystkie kategorie) na ekranie A, a następnie system zostanie przeniesiony do ekranu B (z płaską listą pokazującą tylko elementy wybranej kategorii)
Normalną metodą jest dynamiczne wyświetlanie tylko danych z wybranej kategorii, powiedzmy za pomocą skryptu php (w tym przykładzie jest to sjson.php)
Na ekranie A będzie to wyglądać tak:
Ekran Funkcja GetFlatListItem:
GetFlatListItem (fruit_name) {
this.props.navigation.navigate('ScreenB', {unitserial: fruit_name})
}
Ekran Płaska lista może wyglądać następująco:
data={ this.state.dataSource }
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={({item}) =>
<View style={{flex: 1, flexDirection: 'row', justifyContent:'flex-start'}}>
<Text onPress={this.GetFlatListItem.bind(this, item.serial)} >
{item.ok}</Text>
</View>
Na ekranie B będzie wyglądać tak:
const enstring='https://www.test.com/sjson.php?serial=' + this.props.route.params.unitserial ;
return fetch(enstring)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
a Flatlist może wyglądać następująco:
<FlatList
data={ this.state.dataSource }
>
Pamiętaj, że będziesz musiał zmodyfikować swoje kody, aby dostosować je do swoich potrzeb. Powyższe to tylko przykład.
Najpierw zdefiniuj stany:
const [apiRespPonse, setApiRespone] = useState("");
const [selectedTabData, setSelectedPost] = useState("");
const [category, setCategory] = useState("Sports");
W twoim api call ustaw The Response:
const fetchAllPost = () => {
setApiRespone([]);
const newArr = apiRespPonse.filter(obj => obj.label===category);
setSelectedPost(newArr)
};
W opcji renderowania użyj filtru dla tablicy, aby uzyskać wybrane dane karty:
return (
<View style={styles.app}>
<View style={styles.header}>
<View style={{ flexDirection: "row" }}>
{categories.map((item, index) => {
return <Text onPress={()=>{
setCategory(categories[index])
const newArr = apiRespPonse.filter(obj => obj.label===category);
setSelectedPost(newArr)
}} style={styles.title}>{item.label}</Text>;
})}
</View>
</View>
<FlatList
data={selectedTabData}/>
</View>
);
};
Może to pomoże.
Jeśli dobrze rozumiem, chcesz po prostu odświeżyć płaską zawartość listy z wybraną kategorią. Myślę, że kod, który masz przed aktualizacjami, robi wszystko, czego potrzebujesz, z niewielkimi aktualizacjami. Oto część kodu, którą wystarczy zaktualizować
{categories.map((e,index)=>(
<TouchableOpacity
...
style={[
...
index===page && {
borderBottomWidth:3,
borderBottomColor:colors.primary,
borderRadius:2,
}]
onPress={()=>{setPage(index)}}
>
...
</TouchableOpacity>
)}
Upewnij się, że postsApi.getPosts(page);
zwraca poprawne dane na podstawie podanego page
parametru.