How to Handle Events in a React Native Mac App

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Building React Native Apps for Mac course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Building React Native Apps for Mac, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Building React Native Apps for Mac

On this lesson, we continue adding Dragon Rock's board for our application. On the previous lesson, we connected our native UI component to the Dragon Rock events from the OS. So now the time has come to take care of the JavaScript side of things. I just polished the UI a little bit because this is going to be the last lesson in the course, but nothing else should have changed. Great, so we can start meeting events via our React Native bridge. In order to achieve this, we need to modify our bridging code a little bit. I'm going to go back into Xcode and I'm going to create a new file. This time, not on my lib folder, just directly on the root. It's going to be a Swift file and it's going to be called building apps emitter . I am just going to take the code from the lesson and let's just walk over it. So I'm creating a class. I am going to create a shared instance. So this is the same pattern that you have seen before with the default instances of some of the macOS classes. I don't want you to create a bunch of emitter or I don't want the application to create emitter every time or you need to dispatch an event. I'm just going to give you a shared instance for you to do your operations. Then I'm going to create a native emitter which is going to be our native class . We're going to modify it in a little bit. This is the actual object that can communicate with the JavaScript side of things. So I'm just creating a variable for it. My init function is going to be empty. This is just going to hold this references for me. Then I'm going to create a register emitter function because what I'm going to do is I'm going to take already existing emitter which is going to be created by the React Native bridge and I'm just going to put it inside of my variable. I'm going to have this patch function. This is the function that I'm going to call whenever I want to send an event to the native or to the JavaScript side of things. It's going to take a name, the name of the event and a body which is any payload. We want to be able to send generic payloads to the native side to our JavaScript. Inside I'm going to reach into my emitter and I'm going to call the send event function. I'm going to pass the name that I had before and the body. Right now this is unrecognized because we have a modified native class or native bridge class but we will do it in a little bit. And here I'm just going to create more specific functions. Just so I don't end up sending events which are not recognized by my system. Whenever I'm calling the native side I want to have a little bit more type safety. So I'm just going to create a function that is going to dispatch the file dropped event. It only has the name hardcoded into it and the body is generic as always. Great. So I'm going to save that. Then I'm going to start turning our bridge instance into an event meter. In order to do this first I'm going to go into the bridge file and there is one header file that I need to add to the compilation process. I need to import the RCT event meter class which is not there by default. I'm going to save that and I'm going to go back to my building apps native file on the objective C side of things and I'm going to replace the or I'm going to add the header file that I added into my bridge. So not only I have the bridge module but I have the event meter module in there now and I'm going to change my definition of the external module. So instead of extending a generic NSO object. Now this is going to extend the event meter. So we're basically telling this Swift file that we used before. We're telling React Native that it no longer will hold simple functions that can be called but it will actually turn itself into an event meter. I'm going to save that. And there's one more thing that we need to add into this file which is we need to add an external method and this needs to be called a supported event function. So React Native itself also needs to know which events can you subscribe to. If you try to subscribe to an event that is not there then it will crash. So we need to return an array of the possible values of the possible events that you can subscribe. So now I'm going to go into the Swift file of things and to the Swift part. And I'm going to change first of all the base object that I'm extending. I'm going to change this into the RCT event meter. Afterwards I am going to modify some of these methods. So first I'm going to overwrite my init function. I'm going to call the previous or the super parent which now with the RCT event meter init function. And I am going to call my emitter the class we created before which will hold the reference for this specific emitter. I'm going to reach into the shared instance which should already be created for me. And I'm going to register my emitter. My emitter is going to be this class itself, this instance that React Native is going to create for me. Then I need to add the override extension to this function because the RCT event meter is already has already implemented this. So we're just going to override it. And finally I need to return my list of supported events. So once again I need to override this. And I'm just going to return an array of events that my JavaScript should be able to subscribe to. So this string needs to match our dispatch function. This is also useful for React Native if I try to send an event that's not there then this is not going to work. Great. So in theory if I would compile the app right now it should compile just fine. And we see that it does. Let's just give it a little time. Okay great. So right now in theory everything is set up. So now we just need to start listening for events on the JavaScript side of things. So I'm going to go back into VS Code. And I'm going to go into my building apps native type script file. So here now will be a bigger change. Unfortunately some of the functionality of React Native is based on classes. And we're going to have to modify this file a little bit to use a class. So first I'm going to start by modifying my imports. And I'm going to import some extra classes. I'm going to import my emitter subscription. I'm going to import the native event emitter. And afterwards I'm going to modify the main code. So I'm just going to copy and paste it and we'll walk over it just so I don't make any mistakes. So you see not much changes except that we're using a class. But if you have a key now you'll realize that we now are extending the native emitter class. This is the class that actually can emit the event. And we need to extend it. We cannot just directly bind it because otherwise React Native will send not only the event but actually a bunch of metadata with the object and that's going to crash our application. So basically we subclass the native event emitter. Let me just delete this, this just got in there. And all my previous methods that I created they're still there. They haven't changed. But by extending the native class I should now have access to even more functionality if I try to access here. You will see that I have this at listener method. This is the one that we're going to use to register our listeners for a specific event. So I'm just going to leave it as is. And now I am going to go into my UI store. And we're going to listen for file drops and specifically for image file drops. So I'm just going to get the image from the user and I just want to display it in my UI. So I'm going to create image URL. It starts as null but it can be a string. Next I am going to create. I'm going to go to my the last part of my store. Right after my store has been hydrated this is where we created the code to persist our application. But this is basically the last thing that's going to happen to my store before the UI is ready to be interacted with. So in here I'm going to call my native file. And I'm going to add a listener. I'm going to listen for the same event that I have been created. And I have been creating in Xcode all this time, right, which is the file dropped. And I'm going to create, I'm going to pass a function and this function is going to receive a payload from the native side. In my payload, I'm going to run an action. And I'm going to tell on my store the image URL is going to be equal to my payload URL. If we go back to Xcode and let us quickly look into our state bar button extension, here we can see we're emitting from a shared instance a file dropped function and the URL is going to contain the absolute string for our file. So now what I can do, I'm going to go into my box container and I'm going to display this image to the user. So on the top of my view, I am going to create or I'm first I'm going to check from my root, from my UI store, if there is a image URL in there. If there is an image URL, I want to display it to the user. So I'm going to use an image component, which is part of React Native. The image component takes a source prop in the URI part of the source prop. I'm going to pass my root dot UI dot image URL. And then I am just going to give it a little bit of style. So we already been through this. I want this to be with full. I wanted to have a height of 20 and we can save it. And now has come the moment of truth. If we have I have a very nice image of column here, because that's one of my favorite books. You can see whenever I hover the button highlights itself. And if I drop it, there you go. Right. So let me just change this in order to be a little bit more visible. I'm going to close my dev window. Still not very visible. Let me just add this to 64. I think that works. Great. Right. So this is how we now have drag and drop capabilities. I'm just going to create a random image. I drop it. Perfect. Beautiful. Now we can do a lot of things. The code is not complete, so I cannot drag and drop from the web browser. But you have all the tools in there. If you want to drag and drop from a different application that has a different type of file and creates a different promise for you, you can do that. You can register for drag and drop in URLs. You can register for drag and drop in audio files. You can immediately play the audio file. It's amazing. It's amazing. and