Creating Auth Context0:52
But when we are logged in, it will throw these screens away and these ones will be rendered. So we can tie this together with secureStore, which allows us to persist data on our phone. So let's start with the global state. You can make use of any global state manager within the React ecosystem like Redux or MobX, but I'm just going to make use of React context. So go into our code and let's make a new folder here called context. And within that folder, let's create a new file. Let's call it authProvider. So this will be global state that's related to authentication. And let's export two things here.
So this will be global state that's related to authentication. And let's export two things here. So export const AuthContext. And let's say createContext. And we have to make sure to import that from React. So let's make sure to import React and createContext from React. And let's also export the AuthProvider here. So this is what we want to wrap our entire application in. So any children of this component will have access to the value prop. So let's say export const AuthProvider = function.
So any children of this component will have access to the value prop. So let's say export const AuthProvider equals a function. And let's return. Actually, let me add my {} here. And we'll return the AuthContext.Provider. Okay. Sorry, this should be normal brackets. Okay. And let's close this out. Okay.
And let's close this out. Okay. And remember, the provider has to have a value prop. And this prop will be our global state. So let's add a value prop here. So value equals an object. And for now, let's keep a piece of state called user. And this will hold information about the user, including the token that comes back from the server. So let's go ahead and make a piece of state here for our user, our global user.
server. So let's go ahead and make a piece of state here for our user, our global user. So say const user, setUser equals useState(null). Okay. And for our global state in the value prop, let's go ahead and make those available. So user and setUser. Again, this will hold information about our logged in user, and it will include our token and any other user information that we need globally. So for example, the user's name and their ID.
and any other user information that we need globally. So for example, the user's name and their ID. Okay, let's save that. And we also want to make sure that we render the children here. So let's put it within our AuthContextProvider. So we'll say children. This will just make sure that any child components within this provider will be rendered as well. And we are going to wrap this around our entire application. So we want to make sure to add this. Let's also make sure to destructure here.
Adding Login and Logout3:50
So we want to make sure to add this. Let's also make sure to destructure here. Children. Okay. So let's save that. And let's add a few more things here. Let's add a login method. So this will take in an email and a password. You can also put this on the login screen itself. But I have it here in our global state, which is fine as well.
You can also put this on the login screen itself. But I have it here in our global state, which is fine as well. So this is where we're going to communicate with the back end. So let me just put a comment for now. And we're also going to store the token that we receive in secureStore. So say and store token in secureStore. So we'll worry about this later. But for now, I just want to set the user. So we'll set user. And this is going to be an object when it comes back from the server.
So we'll set user. And this is going to be an object when it comes back from the server. But for now, let's just make it a string. Again, we're just faking it in this video. And let's also make a logOut method. And this doesn't take any params. And for now, all we have to do is unset the user or set it to null. So set user null. So now if you go back to the documentation here, we don't have a Boolean state. We can add this if we like.
Wrapping App with Provider4:58
So now if you go back to the documentation here, we don't have a boolean state. We can add this if we like. But what I'm going to do is just use the user object. So if it's null, then we're not signed in. And if it's not null, then we are signed in. Okay, so let me save that. And now to make use of this context, we have to wrap our entire application in this AuthProvider. So if we do that, then all of this state within the value prop will be available to any of its children.
So if we do that, then all of this state within the value prop will be available to any of its children. So let's go into our app.jsx. And let's wrap this entire thing. So down here. So let's wrap this in an AuthProvider. So let's say AuthProvider here. And I'm going to press tab. And hopefully that imports it. And I think it did.
Handling Loading and Auth Check6:01
So now we can do what they're doing in the documentation and have a separate set of auth screens and a set of app screens based on this global state. So let's do that down here. Let's actually first make a piece of state to check if it's loading. Because when we retrieve information from secureStore, that takes some time. So let's say const isLoading, setIsLoading. And it's a useState. And it's set to true by default, because when this screen renders, it should be loading. Okay. Now let's try to grab this user information in our provider.
Okay. Now let's try to grab this user information in our provider. So this here, let's try to use this within this component here. So we can say const user equals useContext. So let's make sure to import that. And we want to use the authContext. So authContext, this should import it. And let me save that. Okay. Sorry, this should be an object.
Okay. Sorry, this should be an object. So now we're getting this error undefined is not an object. And that's because we're trying to reference this auth context at the same level as the auth provider. And that's not going to work. We want to make sure that this auth provider is one level higher, or at least one level higher. So we have a few options here. If you take a look at the package.json, you can actually set which component is the app.
So we have a few options here. If you take a look at the package.json, you can actually set which component is the app entry point. So if we search for this app entry JS, you can see what's going on there. So app entry, it's actually within the node_modules folder. So you can take a look at that if you like. But for us, I'm just going to make another root component and wrap that instead. So let's take this, let's duplicate it. So let's duplicate this. Let's call it root.jsx.
So let's duplicate this. Let's call it root.jsx. And now in our app.jsx, we can remove all of this. Let's just make a default React Native functional component here. And here's where we can wrap the entire application. So it's one level higher than the root component we just made. So say AuthProvider here, okay, that imported it. And for this component, actually didn't import it properly here. And this component here will just be our root. So we can say root, make sure to import this.
And this component here will just be our root. So we can say root, make sure to import this. And that didn't work. So let's do it manually. Import root from, and it's within the same folder, root, okay. And we don't need view and text. Okay. So let me save this. But now within our root, we can get rid of the authProvider there. So let's remove that within here, authProvider.
But now within our root, we can get rid of the auth provider there. So let's remove that within here, auth provider. Okay, let's get rid of this and the closing one. And hopefully this should work now. And it looks like it does, cool. So now within our root component, let's add a useEffect. So say useEffect. Make sure to import that and let's add a function here. And for now we'll just have an empty dependency list. So it runs when the screen renders.
And for now we'll just have an empty dependency list. So it runs when the screen renders. And let's just make sure that we are getting the correct user. So let's just console.log the user here. And let me just change the user in our auth provider to something that's not null by default. So let me say hello here just to make sure everything is working. So that reloaded. If we check our console, let me just reload this. And I don't have root saved. So let's save that first.
And I don't have root saved. So let's save that first. Save this. Let me reload. And we do get hello here. Okay. So let me just put that back in our AuthProvider. Okay, save that. So here is where we're going to check if the User is logged in or not. So check if User is logged in or not.
So here is where we're going to check if the user is logged in or not. So check if user is logged in or not. We're going to be checking the secure store again, more on secure store later. But it's basically a secure version of local storage in the browser. So say check secure store for the user object or the token. And if we find it in there, we can set the user, we can grab setUser from here as well. setUser. But if we don't find it, then do nothing and the login screens will show. So for now, I'm just going to do a setTimeout because checking secure store does take some time.
So for now, I'm just going to do a setTimeout because checking secure store does take some time. So I'll just do a setTimeout here. And let's just say after two seconds, let's just set isLoading to false. Okay. And here before we render the screens, we can check if we're loading. So if isLoading, then render either a splash page or an activity indicator. So let's just do that. Actually, let me just paste that in. So I'm just returning a view with an activity indicator here that shows that we are checking.
Actually, let me just paste that in. So I'm just returning a view with an activity indicator here that shows that we are checking if we're logged in or not. So let's make sure to import this, save this. And now you see that activity indicator when the app is starting up. Okay, now finally, we can do what we saw in the documentation here. So let me wrap this in a fragment because we're going to do a conditional here. So let's add a fragment here. And instead of isLoggedIn, we're just going to check the user object. So if we are logged in, so if the user object exists, so we can just check the user here.
And instead of is logged in, we're just going to check the user object. So if we are logged in, so if the user object exists, so we can just check the user here and add a question mark and go ahead and render the app screens, which we already have here. So let me just wrap this in parenthesis. So this up to here should be our app screens. And if we're not, let's render the auth screens. So for now, let me just put a view here. Actually, let me just copy this, which should be in the middle, and then we'll replace it with a screen in a second. Okay, let's just put some text here.
with a screen in a second. Okay, let's just put some text here. This is the login screen. Okay, let me save that. And I forgot to close out the { curly brace here. Save that, okay. And there you see this is the login screen because the $user object is not set. So again, I'm going to reload this. There you see the activity indicator, and we get the login screen. Now if I were to set the $user to something that's not null.
Building Auth Screens Navigator12:48
There you see the activity indicator, and we get the login screen. Now if I were to set the user to something that's not null. So let's go to our auth provider and set it to something here, then we should get our normal app screens. So I'm going to save this, activity indicator, and we get the normal app screens. Okay, so let me just put this back to null, and let's go ahead and make that login screen. So let's make a new folder here, or a new file called auth, and then we'll add loginScreen.jsx. So let's just grab what we have here in our root, right here, for now at least. And let's say loginScreen, okay. Let's try to import that.
And let's say login screen, okay. Let's try to import that. Not sure if that's going to work. Anyways, let's just add the login screen here, react-native-functional-component, okay. And let me just paste in what I have in my clipboard, and save this. And let's see if this imports now. Okay, so now it does, cool. Save that, and it should be the same. Now we also want other auth screens, like registration or forgot password. So we have to define another stack here, just like we have a stack for our main app.
Now we also want other auth screens, like registration or forgot password. So we have to define another stack here, just like we have a stack for our main app. So if we go up here, is it here? Yeah, there's one called homeStackNavigator. And we also want a stack navigator for our auth screens. So let's sort of use this as a base, and then just add our screens here, our auth screens. So let's duplicate this, let's call it authStackNavigator, authStackNavigator, okay. And let's make it so that there is no header, so let's change this to false. The first screen will be our loginScreen, so loginScreen. This will be our loginScreen, okay.
The first screen will be our login screen, so login screen. This will be our login screen, okay. And sorry, login screen. And these options are fine. And let's get rid of these, okay. And let's add one more for registration, and you can add more for other auth screens like forgot password, but for now this is fine. So let's say register screen. Let's make that register screen. Let's go ahead and duplicate this.
Let's make that register screen. Let's go ahead and duplicate this. Let's call it register-screen.jsx, okay. Let's change this to register-screen, and let's also change this register, okay. Save that, make sure to import this register-screen here, cool. And now we can make use of this auth-stack-navigator within here instead of our login screen. So we can get rid of this login screen. I think we still need to wrap it in this navigation container, and then we can add the auth-stack-navigator as a component, okay. Let's self-close that, and hopefully this still works, should be the same.
Implementing Login UI and Actions16:00
the auth-stack-navigator as a component, okay. Let's self-close that, and hopefully this still works, should be the same. And it is, cool. Okay, now let's go into our login-screen, and let's add the inputs for email and password. So first let's add some state here, I'm just going to paste it in. So the state will hold whatever the user types in for the email and password. And for the text, let's just say login-screen here, and we'll style it later on, but for now I just want to have generic text inputs. So the first one is email, so I'll say text-input. Let's say on-change-text should be set-email, okay.
So the first one is email, so I'll say text-input. Let's say on-change-text should be set-email, okay. The value is email. Placeholder will be email. Let's change the placeholder text color, let's just make it gray. And let's set the text-content type to email-address. So this is like input type equals email in the browser. So email-address, okay. Let's also set the keyboard type to email-address. So this will make your keyboard have characters specific to an email address.
Okay, it did. Save that. We have to import useDate, okay. And there are our inputs. So this is email, and this is our password, okay. And we also need a button here to log in. So let me just use a generic button for now. So say button. It's this one. That should have imported it as well on press equals.
It's this one. That should have imported it as well on press equals. And let's just say login. Remember we have this login method from our auth context. And we'll import that in a second. Okay. So login, email, and password. And let's give it a title of login. Okay. Let's make sure to grab the login method from our auth context.
Okay. Let's make sure to grab the login method from our auth context. So let's grab that from here. We can say const. We're grabbing the login method. So login equals useContext, okay. And we're grabbing the auth context. Cool. Does that work? Looks like it does.
Let me just reload to make sure. Reload this. Okay. We get the auth screens. But if I hit login, we are now within our app screens. So everything is working. So again, we don't have secure store in place, so nothing is persistent. So if I were to reload this again, the user object would be null. So we should return back to the auth screens. So reload, we get this loading indicator, and we are back at the auth screens.
So we should return back to the auth screens. So reload, we get this loading indicator, and we are back at the auth screens. I'm going to hit this, user becomes not null, and then we should get our app screens. Let's add logout as well. So let's add it within our settings screen. It's kind of hard to just perform an action within this drawer navigator. So I'm just going to put it in the settings screen here. So let's go into our settings screen. Let's add a button here, button title equals logout, and on press, we'll just log out here, and we'll grab this from our context as well.
Let's add a button here, button title equals logout, and on press, we'll just log out here, and we'll grab this from our context as well. Okay. And we can grab it here. So const logout equals useContext, and we are grabbing it from our authContext. Cool. Save that. Can't find Button, make sure to import that. Okay, save that. We're back at the login screen because nothing is persisting.
Okay, save that. We're back at the login screen because nothing is persisting. Let's log in. User is now not null. We're getting our app screens. But if I were to log out, remember, we are just setting the user to null here. So let's do that, log out, and we are back at our auth screens. Cool. And one more quick thing here is that we should be able to navigate within our auth screens here.
And one more quick thing here is that we should be able to navigate within our auth screens here. So let's navigate to the register screen from the login screen. So let's grab the navigation prop from here, and let's add another button underneath login here. And just duplicate this, let's say, go to register page or screen. And we can just do navigation.navigate to the register screen is what I named it, I save that, give that a try. And there is our register screen. So let me just do the same thing here for our register screen.
So that's the basic auth workflow on the client side. Now, obviously, we don't have things like persistent storage with secure store or communication with the server yet, but we'll take a look at that in the next video. So one more quick review. We have our authProvider, which is our global state. We have this user object, which holds information about the user, and will also hold the token that comes back from the server. So it's null by default. But when we log in, we're sending it to a string. And when we log out, we're sending it back to null.
But when we log in, we're sending it to a string. And when we log out, we're sending it back to null. In our root app.jsx, we're just wrapping the entire application in this authProvider. So all components have access to it in our root.jsx. We have some state here to check if the token is in secure store. So we have to check if it's loading first. And in here, we can either show an activityIndicator like we're doing here or show a splash screen. And then in here, we have this conditional to check if we are logged in based on that piece of global state.
And then in here, we have this conditional to check if we are logged in based on that piece of global state. So if we are logged in, then go ahead and show the app screens. If we're not, show the auth screens. And within this login screen, we are just calling that login method and setting the user to not null. And at that point, we should get the app screens. And this should work as before. So I hope that makes sense. Let's go ahead and make a commit here.
So I hope that makes sense. Let's go ahead and make a commit here. So let's stop this. git add, git commit, auth workflow on client.
