Technology Articles

Building In-App Notification Systems in React Native

Notifications are one of the most important parts of any application. In this article, I want to share the experience of implementing a reliable and good looking notification system leveraging the React Native framework which provides a number of powerful tools and techniques such as higher-order componentshooks, and animations.

This article was written by Akvelon Mobile and Front End Developer Vadim Korobeinikov and was originally published on Medium

 

Introduction

Let’s highlight the advantages of some techniques we will use:

  1. Hooks are a powerful mechanism allowing to use state and other React features in functional components and were first introduced in React 16.8. Their benefits are:
  • Reusing stateful logic between components
  • Reducing components complexity
  • Better separation of concerns
  • Optimizations, e.g. better minification and more reliable hot loading

2. A higher-order component (HOC) is another useful technique that we will use in our example. React documentation says that HOC is a function that takes a component and returns a new component. HOCs can be useful for:

  • Injecting dependencies
  • Inheritance inversion
  • Components factory

3. Animations are very important to create a great user experience. They provide users with feedback when they are interacting with the app.

Roadmap

To build a reliable notification system, we will take the following steps:

  1. Implement components for different types of notifications
  2. Add a state management system to store notification objects by implementing a container component where ones will be displayed
  3. Apply animations for better user experience
  4. Apply one of the newest features of React, hooks

This is how the final result will look:

Implementation

1. Notification components

Let’s support a couple of different event types: success, info, warning, error. They will look a little different but share the same core implementation:

Here makeNotification is a HOC. In our case, makeNotification works as a class factory and helps us to avoid code duplication. Now using this function we can create components for the aforementioned types. We just need to pass icon name, and two strings representing colors. This is how easy it is to create a success notifications component

…and for info notifications. Functional components for other notification types are created in a similar manner.

In order to use notificationsStore within a component and make the component testable, we need to apply HOC. Here HOC is used to inject notificationsStore to NotificationControlsInner component. NotificationControls component is a closure and it’s lexical environment keeps notificationsStoreNotificationControlsInner is a function factory. Store part (that stores app state) is:

2. State management

Now we have all the necessary components implemented: components for different notification types, e.g. SuccessNotification, and NotificationControls which is a source of notifications.

It’s time to add code for managing the state of notifications. There are many ways to implement state management using Context API or third-party solutions like ReduxUnstated and so on. In this article, we will use MobX. MobX is an easy-to-use library and requires less boilerplate code. MobX based on three concepts: state, derivations, and actions.

In one of the above code snippets, we injected store to NotificationControlsInner component. That component contains four buttons, which trigger the addition of a new notification for its’ corresponding type. Each button has onPress property. It is a function which will call add function of NotificationsStore.

To ensure observers are always notified of state change, use only actions to make state changes. To prevent accidental changes to publicnotificationsfield, we will configure MobX in the following way:

observed means that the state needs to be changed through actions. There are more options. As the strict mode is enabled any attempt to modify state directly…

…will cause an error.

In our case, NotificationsInner observes the store. It shows notifications from the array from the store. Each time notifications are being removed or added, NotificationsInner is re-rendered.

3. Animations

Work on business logic is completed. Let’s improve UX. Here we are using Animation API of React Native. The Animated library allows us to create powerful and easy-to-build animations.

We will animate the way notifications appear and hide. Since all components are derived from NotificationBase component, it should receive initialization and configuration of animations.

Animations are started by calling start() on animation. start() may take an optional callback which will be called when the animation is done.

To make the component animated we have to use special components, like Animated.View in our code snippet. These components bind the animated values to the properties and make animation optimized.

Our example demonstrates animation happening over time. That’s why Animated.timing() was used. There are two more animation types: decayand spring. Each of them controls the way how your values animate from starting value to final value. We will animate height, opacity and horizontal offset of notification. Our animation will occur over 150 ms and we should map time onto animated value. React Native allows it through use of the interpolate() function. This function maps input ranges to output ranges.

4. Hooks

In our example, all components are functional except NotificationBase one. The thing is that only class components allow us to use lifecycle methods. There we used ComponentDidMount lifecycle method where we started animation. React hooks change the way we handle lifecycles. Now we can build the entire app with functional components only.

useEffect is one of the React hooks. It tells React that the component needs to do something after render. In some cases running the effect after every render might cause a performance problem. In our case, we have to apply this effect only on component mounting to prevent memory leaks. So [] is passed as the second argument to tell React that effect does not depend on any values from properties or state.

Wrapping up

Notifications are among the most important parts of apps. They are intended to inform users of new messages, remind of events and so on. To make this process secure, fully customizable and supportable, the best solution is an implementation of a custom system with features satisfying all the business requirements. To implement such system, we used such powerful tools and techniques as higher-order components, state management provided by MobX, Animated API and applied Hooks API.

The source code is available on GitHub repository. Also, you can run the app on expo.io or on your device.

Vladim Korobeinikov Akvelon

Vadim Korobeinikov is a Mobile and Front End Developer at Akvelon. He has extensive experience in full-cycle development of multi-platform (mobile, web, desktop) business applications based on multiple stacks of technologies (React Native, C#, .NET, Node.js, JavaScript and more).