Preventing False Counts
Table of contents
No headings in the article.
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 useEffect
like 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.