Most apps need a way to unobtrusively display notifications to users as they happen. Suppose you're running a 20% off sale and you'd like to let your users know as soon as they sign in, or maybe after they submit feedback you want to display a thank you message.
Material UI provides a snackbar component which is great for these types of messages, so I'll be using that for this example. That being said, this article is much more about the Context API than Material UI, and swapping out Material UI for any other component library would be very simple with this approach.
Many apps need to trigger messages from dozens of different components. The React Context API makes it dead simple to provide all components access to a shared snackbar so they can trigger these messages without needing to implement separate components for each message. Here's how.
This post assumes you already have a React app set up with @material-ui/core 1.0.0+ and @material-ui/icons 1.0.0+ installed as dependencies.
First, we need to create our Context API provider and consumer components. The provider provides the state of our snackbar, as well as some functions for manipulating that state, to all consumers. This allows all child components to access and manipulate the provider ’s state no matter how deep they are within the component hierarchy. No prop drilling required!
The first step is creating a context by calling React.createContext(). The object returned contains two properties, Provider and Consumer. We use these to build out the components that will manage and interact with the snackbar’s state.
From here on out, when I use the terms “provider” and “consumer”, I’m referring to the SharedSnackbarProvider and SharedSnackbarConsumer components from this section.
As you can see, our provider is a pretty standard component. In its render function, we render a SharedSnackbarContext.Provider component with a value prop. The object passed to the value prop is what consumers will be able to access so they can interact with our snackbar. For lack of a better term, this is the API for our shared snackbar component.
Take note of the TODO on line 41. Eventually we’ll be rendering the actual snackbar within the context provider’s render function as a sibling to its children. This ensures that when the provider is rendered, all children will be using the same exact snackbar component.
Now we need to build the presentation component responsible for rendering the snackbar UI based on the state of the provider. This component will use the consumer to access the properties it needs for rendering.
Here we’ve built a component that renders the snackbar UI in a function within the SharedSnackbarConsumer component. The argument to the function is the value prop object we exposed from our provider. As a result, when the state of the provider is updated, it will trigger the snackbar component to rerender.
Now we can render this component in our provider’s render function.
At this point, we’re almost finished with the infrastructure. There’s just one last thing to do, which is to render the provider within our app. I’m going to place the provider at the root of the entire app so that all children have access to the snackbar.
Now, ButtonA and ButtonB can render their UI and trigger in-app messages without needing to directly receive props from the root of the app! 🤙
In summary, here’s what happening.
- First, we created a context provider component which manages the global state for our snackbar.
- We then created a component that renders the snackbar’s UI based on the state of the provider. This component subscribes to the provider state in its render function via a context consumer.
- Finally, we rendered two buttons that update the state of the context provider with the openSnackbar function, which was passed to them via a context consumer. This results in the changes propagating down to the snackbar component, triggering a re-render.
Material UI provides a number of other snackbar features I did not implement with this example, such as action buttons and color customization. For the sake of simplicity I didn’t add the ability to customize those features. Adding that logic yourself would be a great next step if you’re really looking to learn the Context API!
Thank you so much for taking the time to improve yourself. If you found this article useful in any way, I will always accept a clap or two. While you’re at it, take a few seconds and follow me for more great content:
- A Quick, Practical Use Case for ES6 Generators: Building an Infinitely Repeating Array
- Building a “Mask Toggle” Password Input Component w/ React and Material UI