DEV Community

Cover image for React hook to show notification with Context
Ab. Karim
Ab. Karim

Posted on

React hook to show notification with Context

How to create a custom react hook with context api to show notification dynamically step by step

This code is inspired from Web Dev Simplified's react use context hook video.

We need to create a context file. Im creating a context file src/context/Notification.jsx(I'm using vite and this file contains JSX, so I had to use ".jsx" extension)

Create context

import { createContext } from 'react';

const NotificationContext = createContext();
Enter fullscreen mode Exit fullscreen mode

We need to wrap our code with context's provider, to make this process easy and clean we're going to export a provider component from this context file. It also reduce code complexity to manage state.

import { createContext } from 'react';

const NotificationContext = createContext();

+ export function NotificationContextContainer({ children }) {
+  return (
+    <NotificationContext.Provider>
+     {children}
+    </NotificationContext.Provider>
+  );
+ }

Enter fullscreen mode Exit fullscreen mode

yeah, you're right! something more to do in this file

*Wrap everything in App component with NotificationContextContainer *, it doesn't broke ๐Ÿชš anything

+import NotificationContextContainer from "./context/Notification.jsx"

//...

export default function App() {
 //... Your amazing business logic
 return (
+   <NotificationContextContainer>
    //... Some amazing component here
+   </NotificationContextContainer>
 )
}
Enter fullscreen mode Exit fullscreen mode

Now we have do some more work in src/context/Notification.jsx.

We have to create and manage our Notification state. Notification state will be a object. Pass value prop to Provider in order to access in the app.

+import { createContext, useState } from 'react';

const NotificationContext = createContext();

export function NotificationContextContainer({ children }) {
+const [notification, setNotification] = useState({});
 return (
+  <NotificationContext.Provider value={notification}>
    {children}
   </NotificationContext.Provider>
 );
}

Enter fullscreen mode Exit fullscreen mode

** Now it's the time to setup update logic **
We have to create another context to update the state from anywhere in our code and also wrap the App with context. We had already wrapped our App component with NotificationContextContainer and children is the App component here. Let's wrap children with update context's Provider.

import { createContext, useState } from 'react';

const NotificationContext = createContext();
+const NotificationContextUpdate = createContext();

export function NotificationContextContainer({ children }) {
const [notification, setNotification] = useState({});
 return (
  <NotificationContext.Provider value={notification}>
+      <NotificationContextUpdate.Provider>
        {children}
+      </NotificationContextUpdate.Provider>
    </NotificationContext.Provider>
 );
}
Enter fullscreen mode Exit fullscreen mode

Yeah, you are right!
We have to implement update state function and pass to value prop.

import { createContext, useState } from 'react';

const NotificationContext = createContext();
+const NotificationContextUpdate = createContext();

export function NotificationContextContainer({ children }) {
const [notification, setNotification] = useState({});
+const updateNotification = (data) => {
+    setNotification(data);
+};
 return (
  <NotificationContext.Provider value={notification}>
+      <NotificationContextUpdate.Provider value={updateNotification}>
        {children}
      </NotificationContextUpdate.Provider>
    </NotificationContext.Provider>
 );
}
Enter fullscreen mode Exit fullscreen mode

We are already did everything, but if you want you use that context we have to first export the two context from this file separately, then import useContext hook and this exported context and we are ready to use this context ๐Ÿ˜‘

๐Ÿ˜ฎ That's requires lots of duplicate code and managing that amount of code is so hard.

*What if we export our own hook? * That's a great idea ๐Ÿ’ก
let's do that

+import { createContext, useState, createContext } from 'react';

const NotificationContext = createContext();
const NotificationContextUpdate = createContext();

+export function useNotification() {
+  return useContext(NotificationContext);
+}

+export function useNotificationUpdate() {
+  return useContext(NotificationContextUpdate);
+}

export function NotificationContextContainer({ children }) {
const [notification, setNotification] = useState({});
const updateNotification = (data) => {
    setNotification(data);
};
 return (
  <NotificationContext.Provider value={notification}>
      <NotificationContextUpdate.Provider value={updateNotification}>
        {children}
      </NotificationContextUpdate.Provider>
    </NotificationContext.Provider>
 );
}
Enter fullscreen mode Exit fullscreen mode

The useNotification function using the context with useContext hook and returning notification state and the useNotificationUpdate is doing the same and returning updateNotification.

That's all for this context file.

Now, we need a component to show notification.
Let's create a component called Notification.jsx.

import CloseButton from '../components/button/CloseButton';

export default function Notification({ ...props }) {
  const typeMap = {
    warning: {
      bgClass: 'bg-gradient-to-r from-yellow-300 to-yellow-400',
      fgClass: '',
    },
    error: {
      bgClass: 'bg-gradient-to-r from-red-600 to-red-700',
      fgClass: 'text-white',
    },
    info: {
      bgClass: 'bg-gradient-to-r from-blue-500 to-blue-600',
      fgClass: 'text-white',
    },
    default: {
      bgClass: 'bg-gradient-to-l from-green-600 to-emerald-600',
      fgClass: 'text-white',
    },
  };

  const { bgClass, fgClass } = typeMap["info"] || typeMap.default;

  const removeNotification = () => {};

  return (
      <div
        className={`fixed bottom-5 right-5 rounded-md p-2 px-4 shadow-xl max-w-xs lg:max-w-md flex items-center z-50 transition ${bgClass}`}
        {...props}
      >
        <span
          className={`block font-lato leading-5 tracking-wide text-start text-base ${fgClass}`}
        >
          My Test Text
        </span>
        <span className="inline-block p-2" onClick={removeNotification}>
          <CloseButton />
        </span>
      </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

This component render static notification to screen. We have to make it dynamic.

Before that, add this component to App component (main component).

import NotificationContextContainer from "./context/Notification.jsx"
+import Notification from "./component/Notification.jsx"

//...

export default function App() {
 //... Your amazing business logic
 return (
   <NotificationContextContainer>
+   <Notification  />
    //... Some amazing component here
   </NotificationContextContainer>
 )
}
Enter fullscreen mode Exit fullscreen mode

Let's make the Notification Component dynamic. In this case notification state object contains two property type and text.

First import two hooks we created previously. We should return notification components if notification state contains text property.

We imported notification update context to clear notification. On close button click we have to set notification to blank object {}

import CloseButton from '../components/button/CloseButton';
+ import { useNotification, useNotificationUpdate } from '../context/Notification.jsx';

export default function Notification({ ...props }) {
+const notification = useNotification();
+const setNotification = useNotificationUpdate();

  const typeMap = {
    warning: {
      bgClass: 'bg-gradient-to-r from-yellow-300 to-yellow-400',
      fgClass: '',
    },
    error: {
      bgClass: 'bg-gradient-to-r from-red-600 to-red-700',
      fgClass: 'text-white',
    },
    info: {
      bgClass: 'bg-gradient-to-r from-blue-500 to-blue-600',
      fgClass: 'text-white',
    },
    default: {
      bgClass: 'bg-gradient-to-l from-green-600 to-emerald-600',
      fgClass: 'text-white',
    },
  };

+ const { bgClass, fgClass } = typeMap[notification.type] || typeMap.default;

 + const removeNotification = () => setNotification({});

+return Object.keys(notification).length !== 0 &&  (
      <div
        className={`fixed bottom-5 right-5 rounded-md p-2 px-4 shadow-xl max-w-xs lg:max-w-md flex items-center z-50 transition ${bgClass}`}
        {...props}
      >
        <span
          className={`block font-lato leading-5 tracking-wide text-start text-base ${fgClass}`}
        >
+          {notification.text}
        </span>
        <span className="inline-block p-2" onClick={removeNotification}>
          <CloseButton />
        </span>
      </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

How to show a notification
Just use the useNotificationUpdate hook in any component.

import { useNotificationUpdate } from '../context/Notification.jsx';

export default function AnyComponent() {

  const setNotification = useNotificationUpdate();

  return <button type="button" onClick={() => setNotification({type: "success", text: "some useful text here"})}> Show notification </button>
}

Enter fullscreen mode Exit fullscreen mode

Share your thoughts about this ๐Ÿ˜Š

My Github

Top comments (0)