Handle real-time application notifications in Reactjs

Amarachi Iheanacho - Apr 20 '22 - - Dev Community

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>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

To see the app, we go to https://localhost:3000

React.js index page

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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.

Appwrite Collection level page

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.

Appwrite attribute level page

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> 
      )
    }

Enter fullscreen mode Exit fullscreen mode

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()
    }, [])

Enter fullscreen mode Exit fullscreen mode

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!');

Enter fullscreen mode Exit fullscreen mode

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')
          }
        }

      }, [])
Enter fullscreen mode Exit fullscreen mode

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);
      }
    }

Enter fullscreen mode Exit fullscreen mode

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)
      }

      }
Enter fullscreen mode Exit fullscreen mode

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')
        }
    }
Enter fullscreen mode Exit fullscreen mode

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 a documentID variable. However, if theArray length is less than zero, we want to alert that the database is empty.
  • Deletes the document using the Appwrite deleteDocument() method. This deleteDocument 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>

Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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]);

Enter fullscreen mode Exit fullscreen mode

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>
    );

Enter fullscreen mode Exit fullscreen mode

Here is what our App.jsx file should look like.

https://gist.github.com/Iheanacho-ai/656f05b6e98ca1422633b112f85b37f8

Here is our notifications app.

React notifications app with Appwrite

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:

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .