Chapter 163. Module Introduction
Use React’s useContext hook to manage app-wide state
Chapter 164. Our Target State & Starting Project
In components folder, in UI folder, have a “notification.js” file and a blank file. To see the end result we got o the “_app.js file” and there we want to render a Sled closing Notification component. We need to import this Notification component from the “notification.js” file. The Notification component takes three props called title, message, and status and we set these in the “_app.js” file. If we change the status to success it changes from blue to green and if error then it shows red. We want in app-wide state so we can trigger this from other components while avoid long prop-drilling.
Chapter 165. Creating a New React Context
We create a “store” folder to hold app-wide state files and add a “notification-context.js” file. Inside we have:
import {createContext, useState} from 'react'
const NotificationContext = createContext({
// set dummy data to get better IDE autocompletion
notification: null, // {title, message status}
showNotification: function(notificationData) {},
hideNotification: function() {}
})
export function NotificationContextProvider(props) {
const [activeNotification, setActiveNotification] = useState()
function showNotificationHandler(notificationData) {
setActiveNotification(notificationData)
}
function hideNotificationHandler(notificationData) {
setActiveNotification(null)
}
// create object to bundle for distribution to other components in app
const context = {
notification: activeNotification,
showNotification: showNotificationHandler,
hideNotification: hideNotificationHandler
}
return (
// actually distribute context object to all interested components
<NotificationContext.Provider value={context}>
{props.children}
</NotificationContext.Provider>
)
}
export default NotificationContext
The goal is to wrap all components and content in “_app.js” file with the context but we want a separate component that holds all app-related state. So we create the NotificationContextProvider component in the code above. Now we can wrap everything in “_app.js” file with the <NotificationContextProvider> Component tags.
Note that this is a different approach from RFTROU course in which there was a reducer function and switch statement in the main.js file (The “_app.js” file in a Next JS application). Moving into a sepearte file is likely preferred.
Chapter 166. Adding Context State
Bring in state and fill out functions into the code block above.
Chapter 167. Using Context Data In Components
Now the context is defined and we wrapped all components in the “_app.js” file in the <NotificationContextProvider> tags but the Notification component does not have access to the NotificationContextProvider. This is because it is in the same component. So we need to move Notification into the Layout component and be sure to import it in there as well. We also want to bring in {useContext} from ‘react’ and also import NotificationContext (Not the NotificationContextProvider!) from “../../store/notification-context”
Now in the Layout component add const notificationCtx = useContext(NotificationContext) and underneath that:
const activeNotification = notificationCtx.notification
And if activeNotification is not null, so we have an activeNotification we want to render the Notification component with the data stored. SO we render Notification Component conditionally using:
{activeNotification && (
<Notification
title={activeNotification.title}
message={activeNotification.message}
status={activeNotification.status}
/>}
Chapter 168. Example: Triggering & Showing Notifications
Now that we have everything set up we need to dive into the components to send trigger with appropriate data. From the “newsletter-registration.js” file you import useContext and call. Also import NotificationContext from the store.
const notificationCtx = useContext(NotificationContext)
And under the ref but before the fetch function
notificationCtx.showNotification({
title: "Signing up...",
message: "Registering for Newsletter",
status: "pending"
})
In the second .then in the fetch function, in the function body:
notificationCtx.showNotification({
title: "Success",
message: "Successfully Registered for Newsletter",
status: "success"
})
And add a catch block and another chunk of code like above but tweaked for an error.
Error http codes will not cause the fetch promise to fail so you must set up logic to trigger the catch block yourself. So in the first .then() block:
if (response.ok) {
return response.json()
}
response.json().then((data) => {
throw new Error (data.message || "Something went wrong!")
})
You can intentionally break your mongo connection string credential string to test your code.
Chapter 169. Example: Removing Notifications (Automatically)
Remove in two ways, clickable and timed out after a few seconds, (error messages should not time out). In the “notification.js” file in the main component function add:
const notificationCtx = useContext(Notification)
And down in the JSX add an onClick={notificationCtx.hideNotification} and now the click to remove is working. Set a timeout using useEffect()
useEffect(() => {
if (
activeNotification &&
(activeNotification.status === 'success' ||
activeNotification.status === 'error')
) {
const timer = setTimeout(() => {
setActiveNotification(null)
}, 3000)
return () => {
clearTimeout(timer)
}
}
}, [activeNotification])
Chapter 170. Challenge Solution
Re-watch this video
Chapter 171. Module Summary
There is nothing special to it, leverage the useContext hook the same as you would in NextJS. Could have used redux in a similar way.
Chapter 172. Module Resources