Handle real-time application notifications in Reactjs
Notifications are used to draw a user's attention to a piece of specific information. Notifications are as versatile as they come, with different brands adopting notification mediums like messages, emails icons on the website or application. They are usually employed to engage users and retain website visitors. Other times, notifications are used internally to monitor application metrics and state.
What we will be building
This post will discuss how to manage real-time app notifications in React.js. We will trigger and subscribe to notifications when events in our database occur. This doesn’t require a custom server.
GitHub URL
https://github.com/Iheanacho-ai/appwrite--notifications-app
Prerequisites
To get the most out of this article, we require the following:
- A basic understanding of CSS, JavaScript, and React.js.
- Docker Desktop installed on our computer. Run the
docker -v
command to verify if you have docker desktop installed. If not, install it from here. - An Appwrite instance running on our computer. Check out the documentation to create a local Appwrite instance. We will use Appwrite’s powerful real-time service handle application notifications.
Setting up the React.js application
We navigate to our preferred directory to create a React.js application using this command:
npx create-react-app <name of our app>
After creating our app, we change the directory to our project and start a local development server with:
cd <name of our project>
npm run start
To see the app, we go to https://localhost:3000
Installing dependencies
Installing Appwrite
Appwrite is an open-source, end-to-end, back-end server solution that allows developers to build applications faster. Appwrite provides ready-made tools and systems to build production ready applications.
We install the Appwrite React.js client-side SDK by running this terminal command.
npm install appwrite
Installing react-toastify
React-Toastify is a react framework that allows us to create super customizable notification component.
We run these commands to install react-toastify.
npm install --save react-toastify
#or
yarn add react-toastify
Creating a new Appwrite project
Running a local Appwrite instance gives us access to our console. We go to the local Appwrite instance on whatever port it is started on to create an account. Typically, this is on localhost:80 or as specified during Appwrite’s installation.
On the console, there is a Create Project button. We click on it to start a new project.
Our project dashboard appears once we have created the project. At the top of the page, there is a settings bar. Click it to access and copy the Project ID and API Endpoint. We will use these next.
In the App.jsx
file, we create a new Appwrite instance using the project ID and endpoint.
import {Appwrite} from "appwrite";
import {useEffect, useState } from "react";
import './App.css';
const App = () => {
// Init our Web SDK
const sdk = new Appwrite();
sdk
.setEndpoint('http://localhost/v1') // our API Endpoint
.setProject(projectID) // our project ID
;
return(
<div>Hello World!</div>
)
}
export default Home;
Creating the database collection and attributes
Go to our Console. On the left side of our dashboard, click on Database. Clicking on the Database menu redirects us to a collection page.
We create a collection in our database tab by clicking on the Add Collection button. This action redirects us to a Permissions page.
At the Collection level, we want to assign Read and Write Access with role:all value.
On the right of our Permissions page, we copy our Collection ID, which we need to perform operations on documents in this collection.
Next, we go to our attributes tab to create the properties we want a document to have.
We need a single string attribute of message.
Creating our Notification application.
Our notification application UI consists of three buttons, to create a document, update the document, and lastly, to delete the document.
We go to our App.jsx
and create the buttons with:
import {Appwrite} from "appwrite";
import './App.css';
const App = () => {
// Init our Web SDK
const sdk = new Appwrite();
sdk
.setEndpoint('http://localhost/v1') // our API Endpoint
.setProject(projectID) // our project ID
;
return (
<div className="App">
<button type='button'>Create Document</button>
<button type='button'>Update Document</button>
<button type='button'>Delete Document</button>
</div>
)
}
Handling database interactions
Appwrite only allows signed-in users to read or write to the database as a safety policy. However, Appwrite enables us to create an anonymous session to bypass this policy.
In our index.js
file, we create our anonymous user session using Appwrite's createAnonymousSession
method. We then specify with a useEffect
hook that we want to call our createAnonymousSession
once our app mounts.
//creating an anonymous Session
const createAnonymousSession = async() => {
try{
await sdk.account.createAnonymousSession();
}catch(err){
console.log(err)
}
}
useEffect(()=> {
createAnonymousSession()
}, [])
Next, using the React useState
hook, we want to create two state variables, theArray
, and response
.
- The
theArray
state variable holds the document ID number of every document we will create in our database. - The
response
state variable contains the real-time notification payloads received from Appwrite.
const [theArray, setTheArray] = useState([]);
const [response, setResponse] = useState('Welcome!');
Subscribing to our collection events
Our application displays a notification every time we create, update, or delete documents in our collection. We achieve this by subscribing to our collection to get a payload each time a database action occurs.
const sdk = new Appwrite();
sdk
.setEndpoint('http://localhost/v1') // Your Appwrite Endpoint
.setProject(projectsID) // Your project ID
;
async function createAnonymousSession(){
try{
await sdk.account.createAnonymousSession();
}catch(err){
console.log(err)
}
}
useEffect(()=> {
createAnonymousSession();
if(sdk.account.get !== null){
try {
sdk.subscribe('collections.[collectionID].documents', response => {
setResponse(`The Appwrite ${response.event} event was called`)
});
} catch (error) {
console.log(error, 'error')
}
}
}, [])
In the code block above, we do the following:
- Check if
sdk.account.get
is not null. This check ensures that we have an active user session running before subscribing to our collections channel. - Use Appwrite's
subscribe
method, which receives the channel we want to subscribe to and a callback function as parameters. To understand more about the various channels we can subscribe to, check out Appwrite's docs. - Updates the
response
state variable with the callback function's response.
Listing our documents
In our App.jsx
file, we create a listDocuments
function to retrieve data from the database and populate the theArray
array.
const listDocuments = async() => {
try {
let response = await sdk.database.listDocuments(collectionID);
response.documents.map(document => setTheArray(prevArray => [...prevArray, document.$id]) )
} catch (error) {
console.log(error);
}
}
We use Appwrite's listDocuments
function to retrieve our data, which receives a collection ID parameter. This collection ID is the same ID we obtained from the Appwrite console’s Permissions Page.
The listDocuments
function pushes each of the newly created document's ID into theArray
array.
Creating new documents
In our App.jsx
file, we write our createDocument
function to create default documents in our database. We use this to simulate an event trigger.
const createDocument = async () => {
try{
await sdk.database.createDocument(collectionID, "unique()", {
"message": "Hello World!",
});
listDocuments()
}catch(error){
console.log(error)
}
}
The createDocument
function above does the following:
- Use the Appwrite
createDocument()
method, the collection ID, and the document payload to create a new document. - Calls the
listDocuments
function. - Logs an error if creating the document fails.
Deleting documents in the collection.
In our App.jsx
file, we create a deleteDocument
method to delete a document in our collection.
const deleteDocument = async () => {
if (theArray.length > 0) {
try {
let documentID = theArray[theArray.length - 1]
await sdk.database.deleteDocument(collectionID, documentID);
listDocuments();
} catch (error) {
console.log(error)
}
} else {
alert('database is empty')
}
}
The deleteDocument
function does the following:
- Checks if the
theArray
length is greater than zero. If it is, we want to store the last document ID in our array in adocumentID
variable. However, iftheArray
length is less than zero, we want to alert that the database is empty. - Deletes the document using the Appwrite
deleteDocument()
method. ThisdeleteDocument
method receives a collection ID and the document ID parameter. - Runs the
listDocuments
function. - Logs an error if creating the document fails.
Here is what our App.js
file looks like.
https://gist.github.com/Iheanacho-ai/c3d5edf96cb59b045f4d53376cc5b7fe
Next, we pass these functions to an onClick
event listener on the buttons.
<div className="App">
<button type='button' onClick={createDocument}>Create Document</button>
<button type='button' onClick={deleteDocument}>Delete Document</button>
</div>
Creating our React-Toastify notifications
This section will discuss creating notifications in our application with the react-toastify library.
To use react-toastify notifications in our app, we need to import the ToastContainer
component, the toast
instance, and its CSS styles in the App.jsx
file.
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
Next, we specify the information we want our notification to display with this piece of code below.
const notify = (response) => {
toast(response)
};
useEffect(() => {
notify(response);
}, [response]);
We create a notify
function in the code block above to call our toast
function. This toast
function receives our response variable as a parameter. The response
data will be passed in the toast.
We then use the useEffect
hook to run our notify
function when our response parameter changes.
Finally, we then add the ToastContainer
component to our project.
return (
<div className="App">
<button type='button' onClick={createDocument}>Create Document</button>
<button type='button' onClick={deleteDocument}>Delete Document</button>
<ToastContainer/>
</div>
);
Here is what our App.jsx
file should look like.
https://gist.github.com/Iheanacho-ai/656f05b6e98ca1422633b112f85b37f8
Here is our notifications app.
By clicking each button, we receive a notification of the action. This project simulates this on a single page, more complex applications would utilize this interaction across pages, devices or independent interfaces.
Conclusion
This article discussed using Appwrite’s realtime feature to subscribe to application events.
Resources
Here are some resources that might be helpful: