如何正确有效地呈现对 FlatList 中评论的回复

问题描述 投票:0回答:1

我正在制作一个新闻页面,您可以在其中撰写评论并回复它们。我已经完成了所有逻辑,但我不知道如何正确显示评论和回复。我使用 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

当我使用LayoutAnimation gif时

android react-native design-patterns mobile expo
1个回答
0
投票

屏幕变白一秒钟

也许你可以调查一下

  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 />

© www.soinside.com 2019 - 2024. All rights reserved.