我正在制作一个新闻页面,您可以在其中撰写评论并回复它们。我已经完成了所有逻辑,但我不知道如何正确显示评论和回复。我使用 FlatList 在 ViewLayout 页面上呈现评论,在其中呈现评论 CommentCard,并在 CommentCard 内,当我单击“查看评论”按钮时呈现回复。
整个问题是,当答案列表打开时,屏幕闪烁(屏幕变白一秒钟)。我尝试用动画、手风琴、可折叠来修复它,但一切看起来都非常糟糕(我将附上几个例子)。我不否认也许我实现得不正确,但我只是不知道如何正确地做到这一点:显示答案的逻辑应该转移到ViewLayout还是留在CommentCard中?您能举个例子来说明如何最好地做到这一点吗?
如果成功的话,我想在不复活的情况下完成它。
这是我的代码(我稍微缩短了它,但显示注释的所有逻辑仍然保留):
const ViewLayout = () => {
const route = useRoute();
const navigation = useNavigation();
const { newsId} = route.params;
const {user} = useGlobalContext();
const { showAlert, alertData, showCustomAlert, hideCustomAlert } =useCustomAlert()
const { newsData, comments, isLiked, isLoading, setIsLiked} = useNewsData(newsId,user.uid)
....
if(isLoading && !refreshing){
return (<Loader isLoading={isLoading}/>)
}
return (
<SafeAreaView className="bg-white flex-1">
<FlatList
data={comments}
ref={flatListRef}
keyExtractor={(item) => item.$id}
renderItem={({ item }) => (
<CommentCard
commentId={item.$id}
username={item.user_name}
creatorLogin={item.user_login}
date={item.date}
text={item.text}
likes={item.likes}
comments={item.numComments}
isLiked={item.isLiked}
user={user}
newsId={newsId}
handleDelete={()=>handleDeleteComment(item.$id)}
/>
)}
ListHeaderComponent={() =>(
<View className="w-full">
{/*Шапка*/}
<View className="bg-white pt-8 pb-2 px-4 flex-row justify-between border-b border-gray-200 shadow-md">
<BackButton
handlePress={()=>router.back()}
className=""
black
/>
<View className="flex-row justify-between space-x-2 items-center">
{(user.role=='ADMIN' || user.login==newsData.creatorLogin) &&
<EditButton
handlePress={goToUpdatePage}
className="mr-auto "
backgroundColor="bg-pink-200"
/>
}
{newsData.link ? (
<TouchableOpacity onPress={() => openLink(newsData.link)}
>
<Image
source={icons.globe}
className="w-[40px] h-[50px] "
resizeMode='contain'
/>
</TouchableOpacity>
) : null}
</View>
</View>
{/*Новость*/}
<View className="flex-1 border-b border-gray-200">
<View className="px-5 pt-3 pb-2 flex-col justify-between space-y-4">
<View className="flex-col justify-between space-y-2">
<View className="items-center">
<Image
source={{uri:newsData.imageUri}}
resizeMode='cover'
className="w-full h-[210px] rounded-md"
/>
</View>
<View className="flex-row items-start justify-between space-x-5">
<View className="mt-1 flex-row items-center justify-start space-x-3">
<Text className="text-sm font-msemibold text-primary-800">{newsData.theme}</Text>
<Text className="text-sm font-msemibold text-grey-600">•</Text>
<Text className="text-xs font-msemibold text-grey-600 mt-[2.9px]">{newsData.date}</Text>
</View>
<View className="flex-row justify-between space-x-2">
<View className="flex-row justify-between space-x-1">
<TouchableOpacity
onPress={handleLikeToggle}
className="mt-[-1px]"
>
<Image
source={isLiked? icons.icon_like_pressed : icons.icon_like}
resizeMode='contain'
className="w-[23px] h-[23px]"
/>
</TouchableOpacity>
<Text className="text-sm font-msemibold text-grey-600 ">{newsData.likes}</Text>
</View>
<View className="flex-row justify-between space-x-[2px]">
<TouchableOpacity
className="mb-1"
onPress={scrollToComments}
>
<Image
source={icons.icon_comment}
resizeMode='contain'
className="w-[25px] h-[20px]"
/>
</TouchableOpacity>
<Text className="text-sm font-msemibold text-grey-600 ">{newsData.numComments?newsData.numComments:'0'}</Text>
</View>
</View>
</View>
</View>
<View className="">
<Text className="text-lg text-black-500 font-mextrabold">{newsData.title}</Text>
</View>
<View>
<Text className="text-primary-800 text-sm font-msemibold">Автор</Text>
<View className="mt-1 flex-row justify-start items-center">
<Image
source={icons.creator_icon}
className="w-[25px] h-[25px]"
resizeMode='contain'
/>
<Text className="text-grey-600 text-sm font-mmedium ml-2">{newsData.creatorName}</Text>
</View>
</View>
<View>
<Text className="text-primary-800 text-sm font-msemibold">Опис</Text>
<Text className="text-sm text-grey-600 font-mmedium">{newsData.subtitle}</Text>
</View>
<View className="mb-4">
<Text className="text-sm text-grey-600 font-mmedium">{newsData.text}</Text>
</View>
</View>
</View>
{/* Коментарі */}
<View className="flex-row px-5 pt-8 pb-5"
ref={commentRef}
onLayout={onCommentLayout}
>
<CommentField
otherStyles=""
placeholder="Напишіть коментар..."
value={inputValue}
handleChangeText={handleChangeText}
onSend={handleSendComment}
/>
</View>
</View>
)}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
ListEmptyComponent={() => (
<View className="pb-10">
<EmptyState
title="Коментарів немає"
subtitle="Будь першим(-ою) хто опублікує коментар"
textStyle="text-grey-600"
comments
/>
</View>
)}
showsVerticalScrollIndicator={false}
/>
{showAlert && (
<CustomAlert
title={alertData.title}
text={alertData.message}
buttons={[{
title: 'Oк',
onPress: hideCustomAlert
}]}
visible={showAlert}
onBackdropPress={hideCustomAlert}
/>
)}
</SafeAreaView>
)
}
export default ViewLayout
CommentCard,我在其中制作了渲染回复的逻辑:
import { View, Text,Image, TouchableOpacity, TextInput, LayoutAnimation, UIManager, Platform, Animated, ActivityIndicator} from 'react-native'
import React, { useState,useEffect,useRef } from 'react'
import { icons, images } from '../../constants'
import Icon from 'react-native-vector-icons/FontAwesome';
import CustomAlert from '../Custom Alerts/CustomAlert';
import * as Animatable from "react-native-animatable";
import Accordion from 'react-native-collapsible/Accordion';
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
}
const CommentCard = ({
commentId,
newsId,
image,
username,
creatorLogin,
date,
text,
likes,
comments,
commentsArray,
user,
handleDelete,
handleEdit,
isLiked
}) => {
const { showAlert, alertData, showCustomAlert, hideCustomAlert } =useCustomAlert()
const {replies,isLoading}=useCommentData(newsId,commentId,user.uid)
const dateText = useDateDiffString(date);
const [likedState, setLikedState]=useState(isLiked)
const [updateNumComments, setNumComments]=useState(comments||0)
const [isEditing, setIsEditing] = useState(false);
...
const toggleReplies=async()=>{
//LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
setIsRepliesVisible(!isRepliesVisible)
}
const handleAnswering=()=>{
//LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
setIsAnswering(!isAnswering)
}
....
// ---- Rendering replies
const renderReplies=()=>{
//LayoutAnimation.configureNext(LayoutAnimation.Presets.linear);
return (
<View className="pl-10 mt-2">
{replies.map((reply) => (
<ReplyCard
key={reply.$id}
replyId={reply.$id}
commentId={commentId}
newsId={newsId}
user={user}
username={reply.user_name}
creatorLogin={reply.user_login}
date={reply.date}
text={reply.text}
likes={reply.likes}
comments={reply.numAnswers}
handleDelete={() => {deleteReply(reply.$id)}}
isLiked={reply.isLiked}
handleAnswering={()=>handleAnswering()}
onReplyAdded={() => {}}
/>
))}
</View>
);
}
const renderCommentField=()=>{
return(
<View className="pl-10 mt-2">
<CommentField
otherStyles=""
placeholder="Напишіть коментар..."
contentStyle="w-auto"
value={replyText}
handleChangeText={(e)=>setReplyText(e)}
onSend={sendReply}
/>
</View>
)
}
return (
<View className={`px-5 pb-4 relative`}>
<View className="px-3 py-4 flex-col justify-center items-start border border-grey-border rounded-xl bg-white">
<View className="flex-row justify-between items-start w-full pb-3">
<View className="flex-row justify-between items-center">
<Image
source={images.avatar}
resizeMode="contain"
className="w-[40px] h-[40px] rounded-full"
/>
<View className="flex-col justify-start items-start ml-2">
<Text className="text-xs font-msemibold text-black-500">{username}</Text>
<Text className="text-xs font-mlight text-black-500">{dateText}</Text>
</View>
</View>
<View className="flex-row items-center space-x-1">
<TouchableOpacity
onPress={handleLikeToggle}
>
<Icon
name={likedState ? "heart" : "heart-o"}
size={18}
color={likedState ? "#F95757" : "#4E4E4E"}
/>
</TouchableOpacity>
<Text className="text-xs font-msemibold text-black-500">{likes}</Text>
{(user.login===creatorLogin || user.role==='ADMIN')&&(
<TouchableOpacity onPress={toggleDropdown}>
<Image
source={icons.dots_vertical}
className="w-[21px] h-[24px] mt-[-2px]"
resizeMode="contain"
/>
</TouchableOpacity>
)}
</View>
</View>
{isEditing ? (
<View className="w-full mt-2">
<TextInput
value={editedText}
onChangeText={setEditedText}
multiline={true}
className="text-sm font-mmedium text-black-500 text-left border border-grey-border p-2 rounded w-full"
/>
<View className="flex-row justify-between items-center mt-4 w-full">
<SmallOutlinedButton
containerStyles="border-primary-default min-h-[21px]"
textStyles="text-grey-400"
title="Скасувати"
handlePress={() => {
setIsEditing(false);
setEditedText(text);
}}
/>
<SmallOutlinedButton
containerStyles="border-primary-default min-h-[21px]"
textStyles="text-grey-400"
title="Зберегти"
handlePress={handleUpdateComment}
/>
</View>
</View>
) : (
<>
<Text className="text-sm font-mmedium text-black-500 text-left">{text}</Text>
<View className="mt-3 flex-row items-center w-full justify-between">
<View className="flex-1 flex-row justify-start">
{updateNumComments > 0 && (
<SmallOutlinedButton
containerStyles="border-primary-default min-h-[21px]"
textStyles="text-grey-400"
image={icons.arrow_down}
imageStyles="w-[14px] h-[14px]"
imageContainer="pr-2"
title={isRepliesVisible ? "Сховати відповіді" : `${updateNumComments} відповіді`}
handlePress={toggleReplies}
/>
)}
</View>
{updateNumComments === 0 && <View className="flex-grow" />}
<SmallOutlinedButton
containerStyles="border-primary-default min-h-[21px]"
textStyles="text-grey-400"
title="Відповісти"
handlePress={handleAnswering}
/>
</View>
</>
)}
{/* Выпадающее меню */}
{isOpen && (
<DropdownMenu
isVisible={isOpen}
handleDelete={handleDelete}
isEditing={isEditing}
handleEdit={handleEditComment}
toggleDropdown={toggleDropdown}
/>
)}
</View>
{isAnswering && renderCommentField()}
{isRepliesVisible && renderReplies()} // ---- Rendering replies
{showAlert && (
<CustomAlert
title={alertData.title}
text={alertData.message}
buttons={[{
title: 'Oк',
onPress: hideCustomAlert
}]}
visible={showAlert}
onBackdropPress={hideCustomAlert}
/>
)}
</View>
);
};
export { CommentCard };
现在看起来像这样: 现在gif
屏幕变白一秒钟
也许你可以调查一下
if(isLoading && !refreshing){
return (
<SafeAreaView className="bg-white flex-1 items-center"> <---- add this
<Loader isLoading={isLoading}/>)
}
return (
<SafeAreaView className="bg-white flex-1">
...
因为看起来您正在使用
tanstack-query
来获取数据,并且初始渲染是 <Loader />