Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for React Native notification center with xState v5
Georgi Todorov
Georgi Todorov

Posted on

     

React Native notification center with xState v5

TL;DR

If you just want to see the code, it ishere. Andthis is the PR with the latest changes that are discussed in the post.

Disclaimer

For this example we will be usingReact Native Paper. It greatly helps with design and saves development time. It might take you some extra steps tointegrate, but it is easy and intuitive to use.

Background

After setting the basics of the app architecture, we can now continue with the introduction of other important functionalities. In this post we will have a look at how we handle any sort of in-app notifications/messages that we want to display to the user.

While working on our application, we observed that the ways of providng the user with proper visual feedback grow. To manage all our snackbars/modals/dialogs/banners, we decided to move their orchestration in a separate machine.

Notification Center

ThenotificationCenter machine is spawned on project initilization. Its reference is kept at root level so that it is be available for all spawned children to communicate with.
For this example we are only handling two types of notification feedbacks but it is easily extendable.

exportconstnotificationCenterMachine=setup({types:{context:{}as{snackbar:{type:Extract<NotificationType,"snackbar">;message:string;severity:NotificationSeverity;};modal:{type:Extract<NotificationType,"modal">;title:string;message:string;};},events:{}as|{type:"NOTIFY";notification:Notification;}|{type:"OPEN_SNACKBAR"}|{type:"OPEN_MODAL"}|{type:"CLOSE"},},}).createMachine({context:{snackbar:{type:"snackbar",message:"",severity:"error",},modal:{type:"modal",message:"",title:""},},id:"notification",initial:"idle",on:{NOTIFY:{actions:enqueueActions(({event,enqueue})=>{if(event.notification.type==="snackbar"){enqueue.assign({snackbar:event.notification});enqueue.raise({type:"OPEN_SNACKBAR"});}if(event.notification.type==="modal"){enqueue.assign({modal:event.notification});enqueue.raise({type:"OPEN_MODAL"});}}),},OPEN_SNACKBAR:{target:".snackbar.open",},OPEN_MODAL:{target:".modal.open",},},type:"parallel",states:{idle:{},snackbar:{initial:"closed",states:{open:{on:{CLOSE:{target:"closed"}}},closed:{}},},modal:{initial:"closed",states:{open:{on:{CLOSE:{target:"closed"}}},closed:{}},},},});
Enter fullscreen modeExit fullscreen mode

The machine listens for theNOTIFY event, which based on the type of notification it receives, keeps the latest notification in the context. The notification is then read and displayed/closed by the corresponding component.

Feedback

There are two components that are subscribed to thenotificationCenter reference -NotificationSnackbar andNotificationModal. They expect therefNotificationCenter as prop and based on the state decide whether to show the notification. The components are rendered outside of the so that they are available to all application screens.

exportfunctionNavigation(){const{send,state}=useApp();return(<NavigationContaineronReady={()=>{send({type:"START_APP"});}}ref={navigationRef}><RootNavigator/>{state.context.refNotificationCenter&&(<><NotificationSnackbaractor={state.context.refNotificationCenter}/><NotificationModalactor={state.context.refNotificationCenter}/></>)}</NavigationContainer>);}
Enter fullscreen modeExit fullscreen mode

Communication

The issue with this approach in xState v4 was that we couldn't predict how deep our actor tree would grow. Sending events between siblings and grandparents in was not straightforward. In case we needed to send an event through several levels of hierarchy, each actor should act as a middleman and resend the event to its parent until the final goal is reached.

The new actor system makes it a lot easier with thereceptionist pattern. XState creates implicitly a system, which now gives us a chance to reach out the notification center by sending single event from any child machine.

sendToNotificationCenter:sendTo(({system})=>{returnsystem.get("notificationCenter");},(_,params:{notification:Notification})=>{return{type:"NOTIFY",notification:params.notification,};},)
Enter fullscreen modeExit fullscreen mode

Unfortunately, similarly tosendParent, we loose our type-safety. As a workaround, I'm using a simple util to guarantee that the notification is in the right format:

exportfunctiongetNotificationCenterEvent({},params:{notification:Notification},){return{type:"NOTIFY",notification:params.notification,};}
Enter fullscreen modeExit fullscreen mode
sendToNotificationCenter:sendTo(({system})=>{returnsystem.get("notificationCenter");},getNotificationCenterEvent)
Enter fullscreen modeExit fullscreen mode

After having the action registered with thesetup method, we can simply call it with:

{type:"sendToNotificationCenter",params:({event})=>{return{notification:{type:"snackbar",severity:"success",message:`You've added an item with id${event.output.item.id}.`,},};},}
Enter fullscreen modeExit fullscreen mode

You can check the end results by opening theList screen and play around with the new functionalities.

screenshot 1

screenshot 2

screenshot 3

Conclusion

Leaving all the advantages aside, I was expecting better type-safety with thesendTo action in combination with thesystem.get() method. Currently, the situation is similar to what we can achieve withsendParent. However, the flexibility in communication provided by the receptionist pattern enhances the developer experience.
Secondly, this is the first time I've experimented withenqueueActions() and I'm beginning to see its potential. It is different from what I've been used to, but it can greatly simplify state machines.
Next, I plan to implement a registration wizard/funnel.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Software developer.
  • Location
    Sofia, Bulgaria
  • Joined

More fromGeorgi Todorov

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp