Man: We're going to start with some navigation already set up. We're going to create a stack navigator with two screens. The first will be our home screen, which when we press this, we'll then navigate to our profile. Our profile will have something that can toggle some editing.
Generally, when you press this back button, React Navigation will handle things correctly. This is the hardware back button. Then if I press it again, it would close the application. Except for if we toggle this on and press back, we want to prompt the user that they should save or keep editing their content.
To make this component work throughout your application, we're going to create it as a reasonable component. We'll create a file called back.js, and then just initialize a new component called handle back. Inside of here, we need to import a couple things. The first, we'll import the higher-order component called withNavigation from React Navigation.
Then we'll additionally import BackHandler from React Native itself. To allow this component to operate on our navigation and hook into the lifecycle methods, we call withNavigation and pass in our handle back into this higher-order component. We can now use navigation within this particular handle back.
The other thing we need to do is, instead of returning a view, we'll just return this.props.children. Whatever the user passes in, we'll just re-render it for them. React Navigation provides lifecycle methods. The ones that we care about will be didFocus and willBlur. We'll first set up a constructor. With our constructor set up, we can save off our didFocus listener.
We want to do this in the constructor because this will be before the screen focuses, so we can actually register when the screen that we are rendering inside of actually focuses. We'll say this.didFocus, and also props.navigation which is provided by our withNavigation higher-order component, addListener. The event that we want to listen to is didFocus. That will pass in some payload.
Then we'll have the ability to register something else whenever the screen focuses. We want to register the willBlur. We'll create a component to the mount lifecycle method, set up this.willBlur is equal to this.props.navigation.addListener. We'll do the willBlur. It will also pass in a payload. Then we can register or unregister something out of here.
Finally, we will unregister these particular methods inside of componentWillMount so that we don't have a memory leak. Let's say this.didFocus.remove. Then we'll say this.willBlur.remove. Now that we have this component set up listening to when our screen focuses and blurs, the method that we'll choose is to listen to the BackHandler inside of the focus.
For this particular screen, it will be allowed to control what happens when you press this back button. If that screen blurs, then it will unregister. It will create a function called onBack. Whenever this is called, we'll just return this.props.onBack. Any component can pass in an onBack method, and then this will be returned to our BackHandler listener.
The BackHandler also operates on a listener. We'll say BackHandler.addEventListener. The event that we want to listen to is hardware back press. We'll then just say this.onBack. Then for willBlur, we'll just remove the event listener. We'll say BackHandler.removeEventListener for the hardware back press, and we'll pass in this.onBack.
The final thing we can do is just copy this and paste this into our componentWillUnmount, so whenever our BackHandler or our component is unmounted, we'll just remove the call to this.onBack. Additionally, the reason why we're just calling a simple function here is that when you're passing into BackHandler.removeEventListener, it's actually going to look up the exact function that this.onBack is.
If we passed in this.props.onBack, it would potentially be a different function on next render. Then we would have a memory leak calling multiple different functions. Now that this component is complete, we can actually put it into action. Inside of our profile, we'll import back handle back from back. Then we can wrap our render with it. Let's say handle back and set up our onBack.
Inside of our onBack function is where we now need to do our logic. If this.state.editing, then we'll prompt the user. However, if they aren't editing, then we want the normal flow to continue. If I return false here, this will then return false onBack, and therefore to the event listener.
This tells React Navigation that you did not handle the hardware back button press. If you're to return true, like we'll do inside of here when this.state.editing is triggered, then it will not do anything. You're indicating to React Navigation that you have handled the back button press.
In our case, we could go to the profile. If I toggle editing on and press back, nothing's going to happen because we're returning true here. If I toggle it off and press back, now React Navigation will handle it. Rather than typing out all a bunch of prompt code, I'm just going to paste in our alert. The alert allows you to pass in different buttons.
In our case, if we want the user to keep editing, we'll pass in onPress and we'll just do nothing, because the cancel will then close the alert pop-up. If we want them to be able to cancel editing, we'll just say, "Go home." Then we'll just call our normal this.props.navigation.goBack that is provided by React Navigation.
If we go to our profile, toggle editing on, and press our hardware back button, we'll get prompted. We can say, "Keep editing," and nothing will happen. We can do it again. We can click Go Home. We come back and toggle it on, toggle it off, and now, it's back to the normal flow of letting React Navigation handle things.