0

I fetch an array of posts from an API and then I map through the posts and pass each post to some components. One of such component is the PostComment component. The PostCard and PostActions component render the correct post for each instance. However, the PostComment component renders the content of the last post in every instance. When I console log the content I am passing, I get the correct contents of each post printed out to the console but when I click on the comments button to open the modal where each post comment should be rendered, it is the content (title, body and totalComments) of the last post that is rendered for all five posts.

import { useState } from 'react';
import { nanoid } from 'nanoid';
import { TbSpeakerphone } from 'react-icons/tb';
import { Badge } from '@/components/Badge/Badge';
import { Card } from '@/components/Card/Card';
import { FollowButton } from '@/components/Button/FollowButton/FollowButton';
import { UserAvatar } from '@/components/UserAvatar/UserAvatar';
import { dateFormatCompact } from 'helpers/dateFormatter';
import { PostGeneric } from 'types/communityTypes';
import { PostActions } from './PostActions/PostActions';
import { PostContent } from './PostContent/PostContent';
import { PostMedia } from './PostMedia/PostMedia';
import { PostComment } from './PostComment/PostComment';

interface PostCardProps {
  posts: PostGeneric[];
}

export function PostCard({ posts }: PostCardProps) {
  const [showComments, setShowComments] = useState(false);

  return (
    <article>
      {posts?.map((post) => {
        return (
          <Card key={nanoid()} className="mb-12">
            <div className="md:flex justify-between">
              <div className="inline-flex space-x-1 items-center">
                <UserAvatar size="w-40 h-40" src={post.poster_image} />
                <div className="flex flex-col pl-5">
                  <span className="box-decoration-clone text-active underline font-medium text-[16px] md:text-[20px]">
                    {post.poster_fullname}
                  </span>
                  <span className="text-[15px] text-placeholder pt-2.5 dark:text-rb-dark-800">
                    {dateFormatCompact(new Date(post.created_at))}
                  </span>
                </div>
              </div>
              <div className="flex items-center space-x-6 mt-[33px] md:mt-0">
                {post.is_sponsored && (
                  <Badge type="success">
                    <TbSpeakerphone className="flex-shrink-0 w-9 h-9" />
                    <span className="pl-2 uppercase">Sponsored</span>
                  </Badge>
                )}
                <FollowButton following_id={post.poster_id} renderWithText />
              </div>
            </div>
            <PostContent title={post.post_title} body={post.post_body_text} />
            {post.post_media.length ? <PostMedia media={post.post_media} /> : ''}
            <PostActions
              totalLikes={post.total_likes}
              comments={post.total_comments}
              reposts={post.total_times_reposted}
              id={post.community_id || post._id}
              original_id={post.original_post_id || post._id}
              user={post.poster_username}
              handleClickComment={setShowComments}
            />
            <PostComment
              key={post._id}
              title={post.post_title}
              body={post.post_body_text}
              totalComments={post.total_comments}
              showComments={showComments}
              handleCloseComment={setShowComments}
            />
          </Card>
        );
      })}
    </article>
  );
}

// PostComment implementation
import { Fragment, SetStateAction, useRef } from 'react';
import { Dialog, Transition } from '@headlessui/react';
import { Icon } from '@/components/Icon/Icon';
import { IoMdClose } from 'react-icons/io';
import { CommentList } from './CommentList/CommentList';
import { CommentReply } from './CommentReply/CommentReply';

interface PostCommentProps {
  showComments: boolean;
  handleCloseComment: React.Dispatch<SetStateAction<boolean>>;
  title: string;
  body: string;
  totalComments: number;
}

export function PostComment({
  showComments,
  handleCloseComment,
  title,
  body,
  totalComments,
}: PostCommentProps) {
  const cancelButtonRef = useRef(null);

  return (
    // transition dialog root
    <Transition.Root show={showComments} as={Fragment}>
      {/* dialog component */}
      <Dialog
        as="section"
        className="relative z-10"
        initialFocus={cancelButtonRef}
        onClose={() => handleCloseComment(false)}
      >
        {/* transition dialog backdrop */}
        <Transition.Child
          as={Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-200"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          {/* dialog backdrop */}
          <div className="fixed inset-0 bg-rb-light-200 dark:bg-rb-dark-50 bg-opacity-[0] transition-opacity backdrop-blur bg-blur blur-3xl" />
        </Transition.Child>

        <div className="fixed inset-0 z-10 overflow-y-auto">
          <div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
            {/* transition for dialog panel */}
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
              enterTo="opacity-100 translate-y-0 sm:scale-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100 translate-y-0 sm:scale-100"
              leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
            >
              {/* dialog panel */}
              <Dialog.Panel
                className="relative transform overflow-hidden rounded-[20px] bg-rb-light-200 py-[40px] text-left transition-all sm:my-8 sm:w-full sm:max-w-lg lg:w-[80%] lg:max-w-[65%] z-50 h-[90vh] max-h-full shadow-2xl
              dark:bg-[#000000]"
              >
                <div className="max-h-[85%] flex flex-col">
                  <div className="flex justify-between p-4 md:pl-24 md:pr-8 mb-4">
                    {/* dialog title */}
                    <Dialog.Title
                      as="h3"
                      className="text-mobile-primary font-bold text-primary dark:text-rb-light-200"
                    >
                      {title}
                    </Dialog.Title>
                    <button
                      className="flex items-center justify-center w-[40px] h-[40px] bg-[#F5F5F5] dark:bg-[#22242F] rounded-full"
                      type="button"
                      onClick={() => handleCloseComment(false)}
                      ref={cancelButtonRef}
                    >
                      <Icon alt="close icon">
                        <IoMdClose size={16} color="#B4B4B4" />
                      </Icon>
                    </button>
                  </div>
                  <div className="px-4 md:px-24 flex-grow-1 overflow-y-auto scrollbar-hide">
                    {/* dialog description */}
                    <Dialog.Description
                      as="p"
                      className="text-mobile-secondary-variant text-primary dark:text-rb-light-200"
                    >
                      {body}
                    </Dialog.Description>
                    <div className="mt-4">
                      <h4 className="text-inactive text-user mb-8 dark:text-rb-light-200">
                        {`${totalComments} comments`}
                      </h4>
                      <CommentList />
                    </div>
                  </div>
                </div>
                <div className="absolute bottom-0 w-full px-[10px] md:px-[60px] pb-[40px]">
                  <CommentReply />
                </div>
              </Dialog.Panel>
            </Transition.Child>
          </div>
        </div>
      </Dialog>
    </Transition.Root>
  );
}

0 Answers0