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 components, hooks, and animations.
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.
To build a reliable notification system, we will take the following steps:
- Implement components for different types of notifications
- Add a state management system to store notification objects by implementing a container component where ones will be displayed
- Apply animations for better user experience
- Apply one of the newest features of React, hooks
This is how the final result will look:
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:
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
NotificationControls component is a closure and it’s lexical environment keeps
NotificationControlsInner 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.
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 Redux, Unstated 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
To ensure observers are always notified of state change, use only actions to make state changes. To prevent accidental changes to public
notifications field, 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.
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:
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.
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.
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.
I work as a member of front-end teams in several projects at Akvelon. There we implement cross-platform client apps for iOS, Android and web. Akvelon is a business and technology solutions company that specializes in applying cutting-edge technologies to problems in fields as diverse as mobile technology, finance, and healthcare.
If you would like to work with our strong Akvelon team — please see our open positions.