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();
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>
+ );
+ }
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>
)
}
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>
);
}
** 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>
);
}
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>
);
}
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>
);
}
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>
);
}
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>
)
}
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>
);
}
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>
}
Share your thoughts about this ๐
Top comments (0)