Вкладки категорий React Native - каждая вкладка отображает разные категории
В настоящее время я пытаюсь реализовать экран просмотра, где пользователи могут выбрать определенную категорию из ScrollView, который отображает сообщения, связанные с этой категорией.
Мой api получает все данные поста из бэкэнда, и для каждого поста у меня есть параметр categoryId.
Я пытаюсь сделать, например, одну из моих категорий - спорт. когда пользователь выбирает вкладку спорта, должен отображаться плоский список со всеми сообщениями о спорте.
В этой текущей реализации у меня есть вкладки категорий, и когда я выбираю любую из вкладок, отображаются все сообщения, а не сообщения для этой конкретной категории.
Вот мой код:
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>
</>
);
}
Когда я делаю console.log (сообщения), вот как выглядит мой ответ:
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,
},
Я подумал о том, чтобы изменить свой вызов API, чтобы получить категорию по ее идентификатору, а затем включить все сообщения для каждой конкретной категории, но это не сработало, поэтому я пошел с вызовом API, чтобы получить все сообщения.
Обновить
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}
Я пробовал следующий код, может кто-нибудь сказать мне, правильный он или нет?
Ответы
В качестве примера предположим, что вы сначала щелкаете элемент в Flatlist (показывающий все категории) на экране A, а затем система перейдет на экран B (с Flatlist, показывающим только элементы выбранной категории)
Обычный метод - динамически отображать только данные выбранной категории, например, с помощью сценария php (в данном примере это sjson.php).
Для экрана A это будет так:
Экран Функция GetFlatListItem:
GetFlatListItem (fruit_name) {
this.props.navigation.navigate('ScreenB', {unitserial: fruit_name})
}
Экран Flatlist может быть таким:
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>
Для экрана B это будет так:
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);
});
}
и Flatlist может выглядеть так:
<FlatList
data={ this.state.dataSource }
>
Обратите внимание, что вам нужно будет изменить свои коды в соответствии с вашими потребностями. Это всего лишь пример.
Первое определение состояний:
const [apiRespPonse, setApiRespone] = useState("");
const [selectedTabData, setSelectedPost] = useState("");
const [category, setCategory] = useState("Sports");
В вашем вызове api установите ответ:
const fetchAllPost = () => {
setApiRespone([]);
const newArr = apiRespPonse.filter(obj => obj.label===category);
setSelectedPost(newArr)
};
Для вашей опции рендеринга используйте фильтр для массива, чтобы получить данные выбранной вкладки:
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>
);
};
Может быть, это поможет.
Если я правильно понимаю, вы просто хотите обновить содержимое плоского списка с помощью выбранной категории. Я думаю, что код, который у вас есть до обновлений, делает все, что вам нужно, с небольшими обновлениями. Вот часть кода, которую вам просто нужно обновить
{categories.map((e,index)=>(
<TouchableOpacity
...
style={[
...
index===page && {
borderBottomWidth:3,
borderBottomColor:colors.primary,
borderRadius:2,
}]
onPress={()=>{setPage(index)}}
>
...
</TouchableOpacity>
)}
Убедитесь, что postsApi.getPosts(page);возвращает правильные данные на основе данного pageпараметра.