Creating a Reddit Clone Using React and GraphQL - 18
Tags: react, typescript, graphql, redditclone
Published: 2021-05-03 17:12:22

### This blog post originally posted on my blog site and you can find it [here](https://www.rasikag.com/posts/creating-a-reddit-clone-using-react-and-graphql-18).

From the last post, we added the back-end code. So now we need to add front-end code. Let’s add a separate component to `UpvoteSection.tsx` . In this component, we want to show up and down arrow buttons and current points that the post has. Also want to show that the status of our response here from different colours. If we upvoted it need to show green colour and downvoted need to show in the red colour.

Before adding that change, we are changing both `vote` and `post` methods to match with this. In the`vote` method we first check that we are upvoting or downvoting multiple times. If so nothing happens. If we are changing the vote we are updating the values accordingly. Replace the code in the `vote` from the below code form checking on `userId` .

```javascript
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]
    );
  });
}
```

Then add the new field to `Post` entity. This is not a column. This will only use for keep the `voteStatus` for each post for current logged user.

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

```

Also, need to change the `posts` query to get the data with `voteStatus` of the current user. To do that, replace the code with the below code.

```javascript

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

```

We can get post related data to this component. Because in future there can need some other details from the post that we need to show in here or some other data to make desitions.

So we are moving post related data to a new fragment.

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

```

To match with this change, make a change to `Posts` query in the front-end.

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

```

Now is time to add the `UpvoteSection` . Here is the code for related that section.

```javascript
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>
  );
};
```

We can add this component to `index.tsx` file.

```javascript
<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>
```

Now the most important part. Once we are voted we are updating the current vote count. To do that we are using `readFragment` and `writeFragment` . What happened here, once we voted, we will request the new data from the `graphql` server by, passing `postId` . Once we receive the new values we are updating them. The main benefit is we are not requesting a full data set, but part of it. Here is the relevant `vote` mutation in `createUrqlClient` method. Also to do these we need to add `grapgql-tag`.

```javascript

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
    );
  }
},

```

---

Thanks for reading this. If you have anything to ask regarding this please leave a comment here. Also, I wrote this according to my understanding. So if any point is wrong, don’t hesitate to correct me. I really appreciate you.
That’s for today friends. See you soon. Thank you.

**References:**

This article series based on the [Ben Award - Fullstack React GraphQL TypeScript Tutorial](https://www.youtube.com/watch?v=I6ypD7qv3Z8). This is amazing tutorial and I highly recommend you to check that out.

Main image [credit](https://www.pexels.com/photo/photo-lavender-flower-field-under-pink-sky-1166209/)