[ReactJS] Push notification sử dụng Firebase Cloud Messaging
This post hasn't been updated for 2 years
Xin chào, trong bài viết này mình sẽ thực hiện chức năng push notification trên trang web ReactJS sử dụng Firebase Cloud Messaging (FCM)
Các bước chính
- Khởi tạo project ReactJS
- Tạo project Firebase và setup FCM
- Cấu hình Firebase trong app ReactJS để nhận message push từ FCM
a. Foreground
b. Background
1. Khởi tạo project ReactJS
Để khởi tạo project ReactJS đơn giản, mình sử dụng create-react-app:
npx create-react-app demo-fcm --template typescript
Sau đó chạy thử project:
cd demo-fcm
npm start
Kết quả:
2. Tạo project Firebase và setup FCM
Đăng nhập vào Firebase Console, chọn Add project:
Mình đặt tên là demo-fcm, chọn Continue và Create project
Đợt 1 chút để project được khởi tạo xong, chọn Continue.
Tại trang chủ của project, bấm vào biểu tượng < /> để setup cho Web
Mình đặt tên là demo-fcm-app, chọn** Register app**
Chọn Continue to console
Tại màn hình chính của project, ở menu bên trái, nhấn vào biểu tượng răng cưa, chọn Project settings. Sau đó chọn tab Cloud Messaging
Ở phần Cloud Messaging API (Legacy) (đang hiện Disable), chọn biểu tượng 3 chấm => Manage API in Google Cloud Console
Ở cửa sổ mở ra, chọn Enable:
Sau đó reload lại trang Project settings, phần Cloud Messaging API (Legacy) đã được Enabled và bên dưới là Server key
Tại phần Web Push certificates, bấm vào Generate key pair: (cái này là VAPID key)
Như vậy là đã cấu hình setup xong project Firebase
3. Cấu hình Firebase trong app ReactJS để nhận message push từ FCM
Trước tiên mình sẽ install package firebase, ở đây mình dùng firebase version 8:
npm install firebase@8.2.0
Tạo file constants.ts trong thư mục src để lưu Firebase config:
//src/constants.ts
export const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
authDomain: "your_auth_domain",
projectId: "your_projectId",
storageBucket: "your_storage_bucket",
messagingSenderId: "your_messaging_sender_id",
appId: process.env.REACT_APP_FIREBASE_APP_ID
};
Config lấy từ Project settings, tab General:
a. Foreground
Để nhận foreground message (khi đang ở tab trang web), mình sẽ viết 1 hàm lắng nghe message khi đang focus vào web.
Trong thư mục src, tạo file firebase.ts với nội dung:
import { useEffect } from 'react';
import firebase from "firebase/app";
import "firebase/messaging";
import { firebaseConfig } from './constants';
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
} else {
firebase.app(); // if already initialized, use that one
}
let messaging: firebase.messaging.Messaging;
if (typeof window !== "undefined") {
if (firebase.messaging.isSupported()) {
messaging = firebase.messaging();
}
}
export const getMessagingToken = async () => {
let currentToken = "";
if (!messaging) return;
try {
currentToken = await messaging.getToken({
vapidKey: process.env.REACT_APP_FIREBASE_FCM_VAPID_KEY,
});
console.log("FCM registration token", currentToken);
} catch (error) {
console.log("An error occurred while retrieving token. ", error);
}
return currentToken;
};
export const onMessageListener = () =>
new Promise((resolve) => {
messaging.onMessage((payload) => {
resolve(payload);
});
});
File này mình định nghĩa hàm onMessageListener để hứng message, hàm này có thể được gọi trong useEffect:
Trong file App.tsx, mình thêm 2 useEffect sau:
useEffect(() => {
getMessagingToken();
},[])
useEffect(() => {
onMessageListener().then(data => {
console.log("Receive foreground: ",data)
})
})
useEffect đầu tiên dùng để lấy Registration token từ Firebase
useEffect sau dùng để hứng message được push từ Firebase (lưu ý là useEffect này không có dependencies)
Chạy thử project lên xem nhé:
Trang web sẽ yêu cầu cho phép gửi thông báo, chọn Cho phép, ta sẽ thấy FCM registration token được console.log ra:
Thử bắn message từ firebase (lưu ý là phải đang mở tab của trang web):
Vào Firebase, tại menu bên trái, chọn Engage => Cloud Messaging => Send your first message:
Chọn Send test message, paste token được console.log ra ở trên và chọn +
Chọn Test và message được bắn về trang web:
Như vậy là chức năng push notification foreground đã hoạt động.
b. Background
Để push notification từ background, cần phải có service workers
Ở thư mục gốc, tạo file firebase-messaging-sw.js với nội dung sau:
//src/firebase-messaging-sw.js
import {firebaseConfig} from './src/firebase'
if ('serviceWorker' in navigator) {
const firebaseConfigParams = new URLSearchParams(firebaseConfig).toString();
navigator.serviceWorker
.register(`../firebase-messaging-sw.js?${firebaseConfigParams}`)
.then(function (registration) {
console.log('Registration successful, scope is:', registration.scope);
})
.catch(function (err) {
console.log('Service worker registration failed, error:', err);
});
}
Ở file này mình sẽ register 1 service worker với params là firebaseConfig được import từ constants mà mình trình bày ở phần a.
Trong thư mục public, mình tạo 1 file firebase-messaging-sw.js với nội dung sau:
//public/firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js');
self.addEventListener('fetch', () => {
const urlParams = new URLSearchParams(location.search);
self.firebaseConfig = Object.fromEntries(urlParams);
});
const defaultConfig = {
apiKey: true,
projectId: true,
messagingSenderId: true,
appId: true,
};
firebase.initializeApp(self.firebaseConfig || defaultConfig);
if (firebase.messaging.isSupported()) {
const messaging = firebase.messaging();
const channel = new BroadcastChannel('notifications');
messaging.onBackgroundMessage(function (payload) {
//can not console.log here
channel.postMessage(payload);
});
}
Đây chính là file service worker. File này sẽ initilize Firebase app và hứng message từ background.
Sau khi nhận được background message, mình sẽ muốn console.log ra để xem. Tuy nhiên do luồng service worker chạy độc lập nên sẽ không thể console.log ở trong file này.
Mình sẽ dùng BroadcastChannel để giao tiếp giữa luồng service worker và luồng web:
...
const channel = new BroadcastChannel('notifications');
...
channel.postMessage(payload);
Tiếp theo là ở luồng web, mình nhận data từ BroadcastChannel và console.log ra thôi.
Tại file App.tsx, mình thêm đoạn code vào useEffect đầu tiên, sau khi thêm thì nó sẽ như thế này:
//src/App.tsx
...
useEffect(() => {
getMessagingToken();
const channel = new BroadcastChannel("notifications");
channel.addEventListener("message", (event) => {
console.log("Receive background: ", event.data);
});
},[])
...
Như vậy là trang web đã sẵn sàng nhận background message. Cùng thử xem nhé!
Ở trình duyệt, mình mở sang tab khác, sau đó test push message như ở phần a, và đây là kết quả khi mình quay lại tab trang Web:
Vừa rồi mình đã trình bày chức năng push notification sử dụng Firebase Cloud Messaging (mang tính demo cho phần frontend).
Trong hệ thống thực tế, thường thì chúng ta sẽ lưu registration token dưới database và khi có event thì bắn event sang Firebase thông qua server backend của hệ thống (chẳng hạn như NodeJS thì thường sử dụng firebase-admin)
Cảm ơn mọi người đã đọc bài viết!
All Rights Reserved