DEV Community

loading...
Cover image for Creating a Reddit Clone Using React and GraphQL - 15

Creating a Reddit Clone Using React and GraphQL - 15

Rasika Gayan Gunarathna
Full-stack web developer. Life-long learner. Skater.
Originally published at rasikag.com ・4 min read

This blog post originally posted on my blog site and you can find it here.

From this blog post, we are going to set up the pagination. So we need to seed a lot of data into the database. We can create mock data using mockaroo site and export those data as the SQL query.

Then run the create migration command to generate the migration file in the server application.


npx typeorm migration:create -n FakePost

Enter fullscreen mode Exit fullscreen mode

Inside that migration file, you will see 2 methods called up and down . Add this code line to up method.

await queryRunner.query(`
... mockaroo queries goes here `);
Enter fullscreen mode Exit fullscreen mode

Then change the server index file to run this migration file. You can move that file into migration folder. Add the migration property to createConnection method.


migrations: [path.join(__dirname, "./migrations/*")],

Enter fullscreen mode Exit fullscreen mode

Then below that method add this line of code to run the migration.

await conn.runMigrations();
Enter fullscreen mode Exit fullscreen mode

At this point, we are not returning the text of the post. But we don’t want to show the full text on the home page, on the home page we can only show a limited number of characters.

We can Resolver annotation to @Resolver(Post) and we are adding FiledResolver.


@FieldResolver(() => String)
textSnippet(@Root() root: Post) {
  return root.text.slice(0, 50);
}

Enter fullscreen mode Exit fullscreen mode

Also, we are using cursorPagination method that taking last post timestamp and return the older posts. We can change the posts method to user query builder and return result as the number of limit. Here we are taking 2 parameters as limit and cursor.


async posts(
@Arg("limit", () => Int) limit: number,
@Arg("cursor", () => String, { nullable: true }) cursor: string | null
): Promise<Post[]> {
  // previously we took all the posts

  // return await Post.find();
  // using query builder
  const realLimit = Math.min(50, limit);
  const qb = getConnection()
  .getRepository(Post)
  .createQueryBuilder("p")
  .orderBy('"createdAt"', "DESC")
  .take(realLimit);

  if (cursor) {
  // take the old post using cursor
  qb.where('"createdAt" < :cursor', {
  cursor: new Date(parseInt(cursor)),
  });
  }
  return qb.getMany();

}


Enter fullscreen mode Exit fullscreen mode

Now our back-end code is completed. Now we need to change front-end graphql query to match with these 2 parameters.


query Posts($limit: Int!, $cursor: String) {
  posts(cursor: $cursor, limit: $limit) {
    id
    createdAt
    updatedAt
    title
    textSnippet
  }
}

Enter fullscreen mode Exit fullscreen mode

After changing this graphql query, execute the yarn gencommand. It will update the generated methods to retrieve the posts.

Now we can change the outlook of the post by adding some chakra-ui components.

We can add a header and re-arrange the Create Post link.

<Flex align="center">
  <Heading>Reddit Clone</Heading>
  <NextLink href="/create-post">
    <Link ml="auto">Create Post</Link>
  </NextLink>
</Flex>
Enter fullscreen mode Exit fullscreen mode

There are a few scenarios that we need to handle. One is if there is no data and still fetching, we need to show the below message.

if (!fetching && !data) {
  return <div>there is some error in graphql query</div>;
}
Enter fullscreen mode Exit fullscreen mode

To use fetching we need to assign it from graphql query.


const [{ data, fetching }] = usePostsQuery({
  // ... rest of the code

Enter fullscreen mode Exit fullscreen mode

We can update the post UI as below to show post with its post snippet.


<Stack spacing={8}>
{data!.posts.map((p) => {
return (
  <Box key={p.id} p={5} shadow="md" borderWidth="1px">
    <Heading fontSize="xl">{p.title}</Heading>
    <Text mt={4}>{p.textSnippet} </Text>
  </Box>
);
})}
</Stack> 

Enter fullscreen mode Exit fullscreen mode

Then if there is data we can show a button to load more post. Above ending </Layout> tag add below code.


{data ? (
<Flex>
  <Button onClick={() => { }); } m="auto" my={8} isLoading={fetching} >
    load more
  </Button>
</Flex>
) : null}

Enter fullscreen mode Exit fullscreen mode

Now we are adding the pagination resolver from cache to createUrqlClient . This is the function that append the post in the cache to the new posts.


const cursorPagination = (): Resolver => {
  return (_parent, fieldArgs, cache, info) => {
    const { parentKey: entityKey, fieldName } = info;
    const allFields = cache.inspectFields(entityKey);
    console.log("allFields: ", allFields);
    const fieldInfos = allFields.filter((info) => info.fieldName === fieldName);
    const size = fieldInfos.length;
    if (size === 0) {
      return undefined;
    }

    const fieldKey = `${fieldName}(${stringifyVariables(fieldArgs)})`;

    const isItInTheCache = cache.resolve(entityKey, fieldKey);
    info.partial = !isItInTheCache;
    const results: string[] = [];
    fieldInfos.forEach((fi) => {
      const data = cache.resolve(entityKey, fi.fieldKey) as string[];
      console.log(data)
      results.push(...data);
    });

    return results;

  };
};

Enter fullscreen mode Exit fullscreen mode

We need to set this in to exchange as cacheExchange .


resolvers: {
  Query: {
    posts: cursorPagination(),
  },
},

Enter fullscreen mode Exit fullscreen mode

Now we can add the onClick functionality. We can use the state to set the cursor. First, create the initial statue for the cursor.


const [variables, setVariables] = useState({
  limit: 10,
  cursor: null as null | string,
});

Enter fullscreen mode Exit fullscreen mode

Then once we click the button change the cursor to last post’s timestamp. This way we can load the new posts.


<Button
  onClick={() => {

  setVariables({
  limit: variables.limit,
  cursor: data.posts[data.posts.length - 1].createdAt,
  });
  }}
  m="auto"
  my={8}
  isLoading={fetching}
>
// ...rest of the code

Enter fullscreen mode Exit fullscreen mode

Now we can see it will update the new posts in the view.


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. This is amazing tutorial and I highly recommend you to check that out.

Main image credit

Discussion (0)