Create a Global Notification Component with React and Context! Part 2 (with code)

Albert Kim
7 min readMay 7, 2019

--

Create a Global Notification Component with React and Context! Part 1

For part 1 visit this link:

In part 2 we will continue where we left off in this tutorial.

At the end of part 1 we had set up our project, added routing, bulma, and created all the components we will use.

In part 2 we will create the context our app will use and implement the functionality to make the notification work as intended!

Step 3: Setting up the Context Provider

Next up we will be adding the context provider into our app so that we can share state between components.

Why do we want to do this?

If we want to update the notification component from the main component then we might pass state from main to notification.

But what if we want to update the notification component from the profile component? Then we might pass state from profile to main to notification.

But what if we don’t want to do that? What if main and profile don’t need to interact with each other at all?

We solve this problem by providing a global state to all of the components and having Main, Profile and FlashMessage use this one source of data.

There are several different ways of creating global state/data in your React app. There’s Redux which is a famous library for handling the problem of sharing data between components. There’s MobX which does something similar in a different way.

Then there is the Context Provider which was a long awaited functionality of solving the global state problem. The benefit of using the Context Provider over something like Redux or MobX is that it comes with the core package of React so you don’t have to add any different packages if you don’t want to.

There are 3 parts of adding a context provider in your app.

1. Create the context and create the provider.

2. Wrap your components in the provider so they have access to the context’s state.

3. In the components that use this state, wrap them in a consumer to gain access.

Create the Context and the Provider

export const GlobalContext = new React.createContext();

That is how you create the context. What this does is create a variable called GlobalContext which is a Context type.

With the GlobalContext variable you are now able to call “.Provider” or “.Consumer” on the GlobalContext and wrap your components with it.

How you use Provider and Consumer:

How you expose the “provider state” to the children of the provider.

<GlobalContext.Provider value={this.state}>  {this.props.children}</GlobalContext.Provider>

To access (aka consume) the context state.

<GlobalContext.Consumer>  {(props) => {    console.log(props);    return (      <div className="section">        <p className="title">Hello from main.js</p>        <button className="button">Show notification</button>        <Link className="button" to="/profile">          Profile        </Link>      </div>    );  }}</GlobalContext.Consumer>

To enable “child components” of the context provider, aka wrap your app with the context provider so the components inside the provider have access to its state.

<GlobalProvider>  <Router>    <Route path="/" exact component={Main} />    <Route path="/profile" component={Profile} />    <FlashMessage show={true} />  </Router></GlobalProvider>

In the file GlobalContext.js you will see a React class with state and methods and a render method. This is what Context is, it’s just another React class that allows other components to use its state.

There is the state, which just holds data about the state of the notification (show, message, status), and a method which updates the state.

In the method setMessage, after the state gets updated, you’ll see that there is a setTimeout method call.

What setTimeout does is sets a time to wait once it’s called. After that time is up, then the code inside the setTimeout callback runs. In this case, after 3000 ms, call this.setState to set the showFlash to false.

Why do we need this line of code? Because if this isn’t there, then we manually have to set the showFlash to false every time we show the notification. Since this notification is just to notify the user briefly, we can add this setTimeout line to always close the flash message after 3 seconds every time it’s called.

Then we call render() which just returns the context provider with the class’s state as the value, and it accepts children which are components that get wrapped within it.

Wrap your components in the provider so they have access to the context’s state.

To expose this context provider you just created, you have to add it to your project.

For this project configuration, the main entry point to this React app is the App.js component. This is what the ReactDOM.render() method calls to render an app.

So if we wrap this App component in the context provider we just created, then that makes App a child of the context provider. Remember the line:

<GlobalContext.Provider value={this.state}>  {this.props.children}</GlobalContext.Provider>

If we wrap App in this provider, then that would be the same as replacing {this.props.children} with <App />.

This exposes the provider state to App and everything in App, like the routes and Main/Profile/FlashMessage components.

So to wrap the App, in your App.js file, import the Context Provider and change the render function to:

return (  <GlobalProvider>    <Router>      <Route path="/" exact component={Main} />      <Route path="/profile" component={Profile} />      <FlashMessage show={true} />    </Router>  </GlobalProvider>);

Now Main/Profile/FlashMessage have access to the GlobalProvider state that they share.

In the components that use this state, wrap them in a consumer to gain access.

So we’ve created a global React class that we can use, we’ve made it accessible to the components we want, now it’s time to see the data.

For now let’s just console.log some data.

To use or consume the data from the context provider in your components like Main, you wrap your components in a Consumer wrapper.

return (  <GlobalContext.Consumer>    {(props) => {      console.log(props);      return (        <div className="section">          <p className="title">Hello from main.js</p>          <button className="button">Show notification</button>          <Link className="button" to="/profile">            Profile          </Link>        </div>      );    }}  </GlobalContext.Consumer>);

What this does is return a render prop:

Which exposes the state from the global context as a prop.

If you look at the results from the console.log, you’ll see the state we created in the Context Provider.

Recap

  • Created the context provider which is just a React class with some specific Context properties.
  • Wrapped the App component in the Context Provider
  • Wrapped the other components in a Context Consumer and console.logged the props.

Step 4: Adding the functionality to the components

Alright now we have our global context that we can use to update the FlashMessage component.

What do we want to do with this notification? We want to display it if the showFlash prop is true, and we want to display the message and its status.

If showFlash is false, we return null so that the notification html doesn’t get shown.

So the FlashMessage component will look like:

<GlobalContext.Consumer>  {(props) => {    if (props.showFlash) {      return (        <div          style={{            position: "absolute",            zIndex: 500,            left: "50%",            top: "50%",            transform: "translate(-50%, -50%)",            boxShadow: "4px 4px 10px -4px rgba(0,0,0,0.58)"          }}       >        <div          className={`notification ${          props.status === "success" ? "is-success" : "is-danger"          }`}        >          <p>{props.message}</p>        </div>      </div>    );    } else {      return null;    }  }}</GlobalContext.Consumer>

What’s happening in this component should be pretty straight forward.

className={`notification ${  props.status === "success" ? "is-success" : "is-danger"}`}

This line of code might be confusing. What this is doing is dynamically adding Bulma classes based on the status prop.

If prop.status is “success” then add the class “is-success”, else add the class “is-danger”

Is-success and is-danger are css classes from Bulma that change the color of the notification from green (success) to red (danger).

That’s what that line of code does. We can dynamically add css classes using the template strings and the ternary operator .

Now we want to have our Main and Profile component to be able to use the ShowFlash component when they need to, in our case, when the button gets clicked. Another standard use case would be to show a notification after some async action happens like deleting an item or sending a friend request.

All we have to do is add an onClick handler to the buttons.

<button  className="button"  onClick={() => {    props.setMessage("You are on main", "success");  }}>

We have access to the setMessage method from the props, and we just have to call it with the message and the status.

That’s it.

No need to import the FlashMessage component in the Main or Profile component, just update the global state that FlashMessage is tied to.

Since we added a setTimeout to the setMessage method, we don’t have to worry about making the notification disappear, it will handle that after 3 seconds.

One last thing, you might notice that if you press the button really quickly, then you might experience some unnatural UX. This is because the setTimeout only runs every 3 seconds, not resetting every time you press the button.

To fix this we just need to add a check to see if showFlash is false, then update the state.

if (!this.state.showFlash) {  this.setState({    ...this.state,    showFlash: true,    message,    status  });  // After 3 seconds change the state to  // not show the notification  setTimeout(() => {    this.setState({      ...this.state,      showFlash: false    });  }, 3000); // 3000 milliseconds = 3 seconds}

Recap

  • Changed the rendering in FlashMessage based on the props
  • Added an onClick handler that calls setMessage on the Main and Profile component
  • Added a check in the setMessage method

Next Steps

Next steps to take is to implement this UX with your own code. Try adding it into an existing project or create a new one. Try implementing this in Redux or MobX or Apollo State or another state management library.

If you liked this please consider following me on Twitter, I plan on creating more content and will always post about my new posts on Twitter.

Part 1:

--

--

Albert Kim

Helping freelance writers build their business with a website builder designed for their needs at https://leavewithaweb.site