How to implement notification functionality in MERN like Facebook and Twitter (part 2 : front-end)

Adel Benyahia
4 min readNov 17, 2022

In this part of this course we will implement the front-end of the application using Reactjs

Why is ReactJS?

React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.

For this part of the course i will go quickly, i will explain only the implementation of the notification component (sorry for that)

. Create a useSocketio hook

import { io } from 'socket.io-client';
import { useEffect, useState } from 'react';

const useSocketIo = () => {
const [socket, setSocket] = useState(null);
useEffect(() => {
const connect = io.connect(__Backend__URL___);
setSocket(connect);
}, []);
return { socket };
};

export default useSocketIo;

Replace ‘__Backend__URL___’ with your backend url

. In your header component add this code

const [notificationsLength,setNotificationsLength] = useState(NotificationsLength);
const { socket } = useSocketIo();
const id = '__user__id__' // the user id
useEffect(() => {
let timer;
socket?.on('connect', () => {
socket.emit('setUserId', id);
// Getting first notifications length
socket.emit('getNotificationsLength', id);
socket?.on('notificationsLength', (data) => {
setNotificationsLength(data);
});
timer = setTimeout(() => {
socket.emit('getNotificationsLength', id);
}, 10000); // run every 10 seconds
socket?.on('disconnect', () => {});
});

return () => {
socket?.off('connect');
socket?.off('disconnect');
socket?.off('notifications');
clearTimeout(timer);
};
}, [id, socket]);

....
<button
className={styles.button}
type="button"
onClick={() => {
navigate(location.state?.from?.pathname || '/dash/notifications', {
replace: true
});
}}>
<div className={styles.center}>
{notificationsLength ? (
<MdNotificationsActive size={30} style={{ marginRight: 10 }} />
) : (
<MdNotificationsNone size={30} style={{ marginRight: 10 }} />
)}
{`(${notificationsLength})`}
</div>
</button>

...

How this code work

  1. When the component mount the useEffect run

. it will connect the socket to the backend

. if connected , socket will emit a message of type ‘setUserId’ and with ‘id’ attached

. When received in the back-end, that will add the connected user to the ‘usersio[]’ and response with ‘notificationsLength’

. When ‘notificationsLength’ is received by the front-end:

socket?.on('notificationsLength', (data) => {
setNotificationsLength(data);
});

Then ‘notificationLength’ will show the number of unread notifications for the specific user (with the given id) in the ‘button’ bellow

. A timer will send a ‘getNotificationsLength’ every 10 seconds to refresh the value of ‘notificationLength’

timer = setTimeout(() => {
socket.emit('getNotificationsLength', id);
}, 10000); // run every 10 seconds
socket?.on('disconnect', () => {});

. Cleaning the socket and the timer at the end of the useEffect hook, that will end the connection and close the timer if the component unmounted

socket?.off('connect');
socket?.off('disconnect');
socket?.off('notifications');
clearTimeout(timer);

. in <button> component

. onClick event will navigate to the <notificationsList> component, where we will list the notifications

onClick={() => {
navigate(location.state?.from?.pathname || '/dash/notifications', {
replace: true
});
}}

. Then we will show the nomber fo notification in the button text like this

<div className={styles.center}>
{notificationsLength ? (
<MdNotificationsActive size={30} style={{ marginRight: 10 }} />
) : (
<MdNotificationsNone size={30} style={{ marginRight: 10 }} />
)}
{`(${notificationsLength})`}
</div>

NB: <MdNotificationsActive /> and <MdNotificationsNone /> both are imported from this npm package : react-icons

. In the <NotificationsList /> component

const id ='__id__'
const limit ='__limit__'
const [page, setPage] =useState(0)
useEffect(() => {
setIsloading(true);
const controller = new AbortController();
const getNotifications = async () => {
try {
const result = await axios.post(
'/notifications',
{
id,
limit,
page
},
{
signal: controller.signal
}
);
setData(result?.data);
setError(null);
} catch (err) {
setData(null);
setError(err?.response?.message);
} finally {
setIsloading(false);
}
};
getNotifications();
return () => {
controller?.abort();
};
}, []);

. id : represent the user id , used to fetch notifications with the desired user

. limit: represent the number of notifications per page

. page state represent the current page

. At the end of the component add these buttons to navigate between pages

<div className={styles.center}>
<button
type="button"
disabled={page === 0}
onClick={() => {
setPage((prev) => prev - 1);
}}>
{'<'}
</button>
<div style={{ marginLeft: '5px', marginRight: '5px' }}>
page: {page + 1} / {data?.totalpage}
</div>

<button
type="button"
disabled={data?.totalpage === page + 1}
onClick={() => {
setPage((prev) => prev + 1);
}}>
{'>'}
</button>
</div>

Complete project video

Complete project source code

Learn more

--

--

Adel Benyahia

Web application developer (HTML │ CSS │ JS | ReactJS | NextJS | NestJS | MERN)