React 및 GraphQL - 18을 사용하여 Reddit 클론 만들기

이 블로그 게시물은 원래 내 블로그 사이트에 게시되었으며 여기에서 찾을 수 있습니다.



지난 게시물에서 백엔드 코드를 추가했습니다. 이제 프론트 엔드 코드를 추가해야 합니다. UpvoteSection.tsx  에 별도의 구성 요소를 추가해 보겠습니다. 이 컴포넌트에서 우리는 포스트가 가지고 있는 위아래 화살표 버튼과 현재 포인트를 보여주고 싶습니다. 또한 여기에서 다른 색상에서 우리의 응답 상태를 보여주고 싶습니다. 우리가 찬성하면 녹색으로 표시해야 하고 반대하면 빨간색으로 표시해야 합니다.

해당 변경 사항을 추가하기 전에 votepost 메서드를 모두 변경하여 이에 맞게 변경합니다. vote 방법에서 우리는 먼저 여러 번 찬성 또는 반대 투표를 하고 있는지 확인합니다. 그렇다면 아무 일도 일어나지 않습니다. 투표를 변경하면 그에 따라 값이 업데이트됩니다. vote를 확인하는 아래 코드 형식에서 userId의 코드를 교체합니다.

const { userId } = req.session;

const upvote = await Upvote.findOne({ where: { postId, userId } });

if (upvote && upvote.value !== realValue) {
  await getConnection().transaction(async (tm) => {
    await tm.query(
      ` update upvote
set value = $1
where "postId" = $2 and "userId" = $3`,
      [realValue, postId, userId]
    );

    await tm.query(
      ` update post
set points = points + $1
where id = $2`,
      [2 * realValue, postId]
    );
  });
} else if (!upvote) {
  // has never voted before
  await getConnection().transaction(async (tm) => {
    await tm.query(
      ` insert into upvote ("userId", "postId", value)
values ($1, $2, $3)`,
      [userId, postId, realValue]
    );

    await tm.query(
      ` update post
set points = points + $1
where id = $2`,
      [realValue, postId]
    );
  });
}


그런 다음 새 필드를 Post 엔터티에 추가합니다. 이것은 열이 아닙니다. 현재 로그인한 사용자의 각 게시물에 대해 유지voteStatus에만 사용됩니다.

@Field(() => Int, { nullable: true })
voteStatus: number | null;



또한 현재 사용자의 posts 데이터를 가져오려면 voteStatus 쿼리를 변경해야 합니다. 그렇게 하려면 코드를 아래 코드로 바꿉니다.


// In the posts method add context as parameter
@Ctx() { req }: RedditDbContext
// add the values to replacement array by conditionaly
if (req.session.userId) {
  replacement.push(req.session.userId);
}
let cursorIdx = 3;
if (cursor) {
  replacement.push(new Date(parseInt(cursor)));

  cursorIdx = replacement.length;
}
// change the query to below query
SELECT p.*,
json_build_object(
'id', u.id,
'username', u.username,
'email', u.email
) creator,
${
req.session.userId
? '(SELECT value FROM upvote WHERE "userId" = $2 AND "postId" = p.id) "voteStatus"'
: 'null AS "voteStatus"'
}
FROM post p
INNER JOIN public.user u ON u.id = p."creatorId"
${cursor ? ` WHERE  p."createdAt" < $${cursorIdx}` : ""}
ORDER BY p."createdAt" DESC
LIMIT $1



이 구성 요소에 대한 게시물 관련 데이터를 얻을 수 있습니다. 앞으로 여기에 표시해야 하는 게시물의 다른 세부 정보나 목적지를 만들기 위해 다른 데이터가 필요할 수 있기 때문입니다.

따라서 게시물 관련 데이터를 새 조각으로 이동합니다.

fragment PostSnippet on Post {
  id
  createdAt
  updatedAt
  title
  textSnippet
  points
  voteStatus
  creator {
    id
    username
  }
}



이 변경 사항과 일치시키려면 프런트 엔드에서 Posts 쿼리를 변경하십시오.

query Posts($limit: Int!, $cursor: String) {
  posts(cursor: $cursor, limit: $limit) {
    hasMore
    posts{
      ...PostSnippet
    }
  }
}



이제 UpvoteSection를 추가할 시간입니다.  . 다음은 해당 섹션과 관련된 코드입니다.

interface UpvoteSectionProps {
  post: PostSnippetFragment;
}

export const UpvoteSection: React.FC<UpvoteSectionProps> = ({ post }) => {
  const [loadingState, setLoadingState] =
    (useState < "upvote-loading") |
    "downvote-loading" |
    ("not-loading" > "not-loading");
  const [, vote] = useVoteMutation();
  return (
    <Flex direction="column" justifyContent="center" alignItems="center" mr={4}>
      <IconButton
        onClick={async () => {
          setLoadingState("upvote-loading");
          await vote({
            postId: post.id,
            value: 1,
          });
          setLoadingState("not-loading");
        }}
        isLoading={loadingState === "upvote-loading"}
        aria-label="upvote post"
        colorScheme={post.voteStatus === 1 ? "green" : undefined}
        icon={<ChevronUpIcon />}
      />
      {post.points}
      <IconButton
        onClick={async () => {
          setLoadingState("downvote-loading");
          await vote({
            postId: post.id,
            value: -1,
          });
          setLoadingState("not-loading");
        }}
        isLoading={loadingState === "downvote-loading"}
        colorScheme={post.voteStatus === -1 ? "red" : undefined}
        aria-label="downvote post"
        icon={<ChevronDownIcon />}
      />
    </Flex>
  );
};


이 구성 요소를 index.tsx 파일에 추가할 수 있습니다.

<Flex key={p.id} p={5} shadow="md" borderWidth="1px">
  <UpvoteSection post={p} />
  <Box>
    // add this before post title.
    <Heading fontSize="xl">{p.title}</Heading>
    <Text>posted by {p.creator.username}</Text>
    <Text mt={4}>{p.textSnippet}</Text>
  </Box>
</Flex>


이제 가장 중요한 부분입니다. 투표가 완료되면 현재 투표 수를 업데이트합니다. 이를 위해 우리는 readFragmentwriteFragment  를 사용하고 있습니다. 여기서 무슨 일이 일어났는지, 투표를 마치면 graphql 를 전달하여 postId 서버에서 새 데이터를 요청할 것입니다. 새 값을 받으면 업데이트합니다. 주요 이점은 전체 데이터 세트가 아니라 일부를 요청한다는 것입니다. 다음은 vote 메서드의 관련 createUrqlClient 돌연변이입니다. 또한 이를 수행하려면 grapgql-tag 를 추가해야 합니다.


import gql from "graphql-tag";

vote: (_result, args, cache, info) => {
  const { postId, value } = args as VoteMutationVariables;
  const data = cache.readFragment(
    gql`
    fragment _ on Post {
      id
      points
      voteStatus
    }
    `,
    {
      id: postId,
    } as any
  );

  if (data) {

    if (data.voteStatus === value) {
      return;
    }
    const newPoints =
    (data.points as number) + (!data.voteStatus ? 1 : 2) * value;
    cache.writeFragment(
      gql`
      fragment __ on Post {
      points
      voteStatus
      }
      `,
      { id: postId, points: newPoints, voteStatus: value } as any
    );
  }
},




읽어주셔서 감사합니다. 이와 관련하여 질문할 사항이 있으면 여기에 댓글을 남겨주세요. 또한 내 이해에 따라 이것을 썼습니다. 따라서 잘못된 점이 있으면 주저하지 말고 저를 수정하십시오. 정말 감사합니다.
오늘의 친구들을 위한 것입니다. 곧 봐요. 고맙습니다.

참조:

이 기사 시리즈는 . 이것은 놀라운 튜토리얼이며 확인하는 것이 좋습니다.

기본 이미지credit

좋은 웹페이지 즐겨찾기