Preventing False Counts

Prince Shammah

Prince Shammah / October 04, 2023

––– views

If you read my last post about adding a view counter good for you. Now you can see your page views updated in real-time, but the counts are being updated with every refresh. Didn't notice it yet? Okay, hit refresh. See it now? Great. We can fix this easily using Cookies.

For this problem, I'll be using the js-cookie package. We can install it using this command:

npm i js-cookie

This is what our ViewCounter.tsx component looks like right now:

import { useEffect } from 'react';
import useSWR from 'swr';

import fetcher from 'lib/fetcher';
import { View } from 'lib/types';

type Props = {
  slug: string;
  isCard: boolean;
};

const ViewCounter = ({ slug, isCard }: Props) => {
  const { data } = useSWR<View>(`/api/views/${slug}`, fetcher);
  const views = new Number(data?.total);


  useEffect(() => {
    if(!isCard){
    const createView= () =>
      fetch(`/api/views/${slug}`, {
        method: 'POST'
      });

    registerView();}
  }, [isCard, slug]);

  return <span>{`${data?.total! > 0 ? data?.total.toLocaleString() : '–––'} views`}</span>;
};

export default ViewCounter;

We need to add the Cookies from js-cookie

import { useEffect } from 'react';
import Cookies from 'js-cookie'; // Import js-cookie
import useSWR from 'swr';

Next, we need to check if the page has been visited before.

  // Check if the slug exists in cookies
  const isVisited = Cookies.get(slug) === 'true';

Now I'm going to add something to make sure our counter doesn't keep incrementing in development mode but only in production.

  const shouldCount = process.env.NODE_ENV === 'production' && !isCard && !isVisited;

Now we can update our useEffectlike so:

  useEffect(() => {
    if (shouldCount) {
      // Set a cookie to mark the slug as visited
      Cookies.set(slug, 'true', { expires: 7 }); // Set an optional expiration date

      // Make the POST request to count the view
      fetch(`/api/views/${slug}`, {
        method: 'POST'
      });
    }
  }, [shouldCount, slug]);

This code will add a cookie with the page slug as the name in our device storage and increment the counter.

Our final code should look like this:

import { useEffect } from 'react';
import Cookies from 'js-cookie'; // Import js-cookie
import useSWR from 'swr';

import fetcher from 'lib/fetcher';
import { Views } from 'lib/types';

type Props = {
  slug: string;
  isCard: boolean;
};

const ViewCounter = ({ slug, isCard }: Props) => {
  const { data } = useSWR<Views>(`/api/views/${slug}`, fetcher);

  // Check if the slug exists in cookies
  const isVisited = Cookies.get(slug) === 'true';

  const shouldCount = process.env.NODE_ENV === 'production' && !isCard && !isVisited;

  useEffect(() => {
    if (shouldCount) {
      // Set a cookie to mark the slug as visited
      Cookies.set(slug, 'true', { expires: 7 }); // Set an optional expiration date

      // Make the POST request to count the view
      fetch(`/api/views/${slug}`, {
        method: 'POST'
      });
    }
  }, [shouldCount, slug]);

  return <span>{`${data?.total ? data.total.toLocaleString() : '–––'} views`}</span>;
};

export default ViewCounter;

Now your page views won't be messed up just because someone refreshed it 5 times.