[MUSIC PLAYING]
SPEAKER 1: Hello, and welcome back for lecture seven.
This week, we'll be talking about data.
So in the previous lecture, we had a couple of guest
lectures-- if you remember Brent and Eric from React Navigation.
They talk through the libraries that they wrote.
They talked through a few of the different navigators that they have.
One was the switch navigator, which was very similar to a construct
that we learned in the previous lecture before that, whereby
you have one screen, and then it will completely switch to another screen,
and the first screen would be completely unmounted.
They talked about the navigation prop, which
is a prop that's passed down to every single navigator.
On it are things such as params that you can pass into each navigator, or maybe
the navigate method, which allows you to navigate to a different screen.
They talked about stack navigator, which was the navigator that
allowed you to navigate to new screens.
And new screens would actually get pushed
onto a stack, which gives you the ability to pop or go back.
We also talked about configuring navigators,
which was allowing you to maybe change the header color
or put some buttons at the top.
They talked about a tab navigator, which was either a tab bar on the bottom
or on the top, which allows you to swap between two screens.
And then lastly, we talked about composing navigators,
which was nesting navigators.
So maybe having a stack navigator within a tab navigator, or the like.
In this lecture, we'll be talking about data.
Up into this point, all of the applications
that we've been doing in class have been self-contained,
where all of the data that we need for the app
is either generated within the app or created during the time of the app.
But not all apps are self-contained.
Any app that wants to rely on information not computed within the app
itself needs to get it from somewhere.
And where can we get it from, or how do we go about getting that data?
Well, we end up communicating with other resources using an API.
And so how might we do that?
This is similar to a problem that we faced so far.
Maybe you have two components that need to talk to each other.
And how might we get those components to talk to each other?
So if you imagine what a application looks like, its a tree--
a tree of React elements.
So maybe you have your app, and maybe you
have something over here that's maybe a form.
And maybe we have an input, and another input.
So what happens if we want these two inputs to talk to each other?
Or maybe one input is dictated by the value of the other input.
And so we want to somehow have these communicate with each other.
It's not very React-like to have them directly talk to each other.
In React world, the way we generally do that is by passing
prompts down to things.
And so where might we be able to put that logic in order
for these two childs to talk to each other?
Well, we need to look up the tree.
We need to maybe do that in a shared parent.
And so in order to have these two inputs talk to each other,
or at least have them influenced by the value of the other input,
we need to put that logic in a shared parent of the two.
And so all that logic would go here within their shared parent.
And so we can kind of draw an analogy if we want
applications to talk to each other.
So maybe I have an application on my phone,
and David has an application on his phone.
How are we going to get those to talk to each other?
And so you imagine this tree exists, but also some other tree exists.
And so maybe we have my phone and David's phone.
So how are these things going to talk to each other?
So in the React way, the way we did that, was we
had these two components talk to each other via a shared parent.
And the same thing could be true with these two phones.
We need to have them talk to each other through some sort of shared parent.
And maybe that shared parent is some server, or something up in the cloud.
And the way we talk to that is via an API.
And so if we want these two phones to talk to each other,
we need to have them talk through some shared parent.
And the way that we'll talk to that parent
is through this thing called an API.
And so what exactly is an API?
Well, it stands for an Application Programming Interface.
What the heck does that mean?
Well, it's a defined set of ways with which a resource can be interacted.
We've seen APIs thus far.
So every single React component actually has an API.
And how do you interact with these React components?
Well, you pass them prompts.
And what dictates the prompts that they want to see?
Well, it's just the way that that component is implemented.
What other sorts of things have we seen in class that have APIs?
Well, class does.
If you remember, when we write classes, these classes have methods.
And how do we interact with that class?
Well, we invoke those methods.
And so those methods are actually an API with which
you can interact with those classes.
And just like these things have APIs, so do web services.
And so say there's some server stuff in the cloud,
and the way you want to talk to it would be using its API.
And so how are APIs created?
Well, oftentimes providers-- or in other words,
the people who create those APIs--
a lot of times, they get to decide what the API looks like.
But that's not always true.
Can anybody think of an example where an API is decided for you?
I'll give a hint, we've been using it all class.
JavaScript is actually an example of this.
So the people who write JavaScript don't necessarily get
to decide on what that API looks like.
It's actually dictated for them by Ecma.
So if you remember back to ECMAScript, and when we talked about things
like ES6 and ES7, those are actually specifications for this language
that JavaScript then implements.
And so the JavaScript API is actually dictated for them
by these ECMAScript proposals.
And for US consumers, or the people who actually use
those APIs, in order for us to know exactly what the APIs are,
we actually need to look at the docs.
And so we need to just read the docs in order to know how to use these APIs.
And so let's actually look at an API that's running right now.
And so this link here, randomuser.me, is an API
that's free and runs up in the cloud.
And so say we wanted to go use this thing, this API called randomuser.me.
How do I know how to do it?
Well, it turns out they have a pretty decent documentation page.
And if you scroll down, you can see how to use.
The way to use this random generator API is
to use Ajax, which we talked about in the CS50.
Asynchronous JavaScript and XML.
So in other words, we can send fetches without refreshing the whole page.
And so ignore this usage of that Ajax.
We're going to use this thing called fetch in a little bit.
But this will show you how to use this API.
So say we want to get a bunch of random users.
The way we do that is we visit this URL.
So if we visit https://randomuser.me/api/?results=5000,
we will actually get 5,000 results back.
And so let's just visit that URL.
So if I just copy that, open a new tab, and paste that,
I get a bunch of this stuff called JSON.
Or in other words, JavaScript Object Notation.
And if you look closely, you see results.
This first person's, their gender is female,
their name is Miss Nicole Ramos, their location is at this address,
they live in the city, and a bunch of other information
about this particular user.
And if we scroll down, we'll see 5,000 other randomly-generated users.
And so what else can we do?
Well, we can actually specify a gender.
If we only wanted females, we can do gender=female.
So if we rather than doing results-5000 did something like gender=female--
and if I spelled female correctly--
then we'll see we get a bunch of people, and every single one is female.
Hmm, I wonder what else this API can do.
We can dictate what these passwords look like.
So every single user that comes back has a password.
And we can actually decide what that password
looks like by passing in some parameters to this API.
What else can we do?
We can do seeds.
So if we want to get the same results back every time, we can pass in a seed,
and we're guaranteed to get the same results.
We can determine the format, so we can get JSON or CSV, YAML, XML.
We'll stick with JSON.
We can to use previous versions of the API, which we don't really care about.
And lastly, we can determine nationalities.
And so if you look through here, you might
see some people that don't have American-formatted names,
or maybe their date of births are not formatted in the same way,
or maybe their phone numbers are formatted slightly differently.
And I don't see one immediately scrolling-- oh, here we go.
This is not English.
And so it might mess up our app.
And so we can dictate exactly what nationality we want by passing this
in, nat=GB, or in our case, US.
So if we do nat=US, then we're guaranteed to get back only people with
nationalities from the United States.
And so how do we actually go about making these network requests?
Well, it turns out this thing called fetch
is polyfilled for us in React Native.
What does polyfilled mean?
Well, it means that this method may not be natively part of JavaScript,
but it's implemented to match the usage of the fetch method in the browser.
And so fetch is a function that comes in all browsers,
and it allows us to make network requests asynchronously.
But it's not natively part of the JavaScript spec.
And so what a polyfill is is basically a reimplementation
of that same specification.
And so the people who maintain React Native actually re-implemented
fetch for us.
And puts it in the global scope so that we are allowed
to use it anywhere in our React Native.
And so how do we use fetch?
Well, it expects a URL, and optionally some configuration.
So let's actually go and use it.
So let me just open up a browser console,
and I can do fetch, and then give it a URL.
And so let's just use this URL here.
Going to do fetch that URL, enter, and I see this thing promise pending.
What does that mean?
So fetch returns a promise, which is fulfilled with a response object.
And if you want to look at the documentation for fetcher response,
you can do it these links.
But what is a promise, and how do you use promises?
Well, this is something that we talked about in an earlier lecture.
Promises allow us to write asynchronous and non-blocking code,
and it allows us to change callbacks and/or error handlers.
And how you do that?
Well, there's this thing called .then, which executes the callback that is
passed in as its argument after the previous promise block returns.
Analogously if there's an error, then whatever we pass to .catch is executed
with that error.
And so let's actually go ahead and use that.
And so if we wanted to do something like fetch and actually play with
the response that comes back, we're going to have to put a .then following
this.
So we can do .then.
We're going to get some sort of response.
And let's just console.log that response.
Well now, it goes ahead and does this fetch, returns a promise.
And we see it console.log this response here.
And if we inspect it, we see a bunch of stuff.
We see body, bodyUsed false, headers, and in it
are any headers, ok true, redirected false, status 200, type basic, URL.
What the heck is all of this stuff?
Well, if we were just to look at the documentation for response,
we can see exactly what comes back as a response object.
I happen to know exactly what this response object looks like,
and I can do something with that response called response.json.
And what that does is it actually unwraps the JSON that
comes down in that response.
And what does it return?
Well, it returns another promise.
And so we can chain another .then here with that.
We can call it result, and go ahead and console.log the result.
And now we see this console.logged.
Results is in array one, and some info.
And if we look at the results, we can see this one person.
So what if we wanted to maybe get more users back?
Well, we're going to have to go check the documentation
and see how we get more users.
And so if you remember at the very top, we
could actually pass in a number of results and get that number of results
back.
And so if we are again to do random user, and instead of doing nat US--
or in addition, we can do results=50.
I'll put an and there to chain these, and then send that,
and we get back now an array of 50 people.
And if we look at the results, we can see each person
and inspect that object.
And if you want to look at more usage of promises,
you can visit both documentation at that link.
And as we talked about in an earlier lecture,
there is actually an alternative to promises now.
So rather than doing .then, .then, .then, .catch,
which might be confusing if you're not used to thinking asynchronously,
we can actually use this new thing called async and await.
And what this allows us to do is to write asynchronous code
as if it were synchronous.
And it's important that I say, as if it were synchronous.
Because that doesn't necessarily mean that it is in fact synchronous.
This is still non-blocking.
And so if we want to use this, then we can just mark a function as async,
and it will return a promise.
And within an async function, what we can do
is we can await the value of another async function or promise.
What does that look like?
Well, rather than just doing this thing called fetch,
let's write an async function called fetchUsers.
And in that function, what are we going to do?
Well, first we're going to send off this fetch.
So we can do fetch this.
And we're actually going to assign it to a variable.
So let's call that results, or response.
So we have this const response equals fetch this thing.
But since fetch is a promise, we can actually
tell JavaScript to wait for the value of that promise.
So we can put await.
And what that will do is it will wait for that promise to resolve, and then
set this response variable to that result. And I put it in air quotes,
because it's not actually waiting and halting the program.
It's just doing that behind the hood.
It's still all asynchronous and non-blocking.
Well, now that we have a response, now we
have to get the JSON out of that response.
And so just like above, we did this thing called response.json.
We can do const json or result equals response.json.
And again, since response.json returns a promise,
we just have to wait for that promise to fulfill.
And so if we put that await keyword in there, then
now result is going to get assigned the response.json when
that returns from this promise.
And now what?
Now we have the actual result. And so now
if we want to do the same exact thing as we did here, we can just
do console.log that result. And now if we go ahead and invoke fetchUsers,
we see that the promise has returned, and we see
all of the results printed out here.
And so these two things are functionally the same, this long fetch, .then,
.then, and this async function fetchUsers.
And if you notice, this one looks a lot like code that we've written thus far.
It looks very synchronous in nature, but behind the hood,
it's doing a bunch of asynchronous things.
But what happens if an error occurs?
So in promises, we do it with a .catch.
But there's no .catch in this async thing.
So in order to do that, we can actually use a try catch block.
So if we wanted to add error handling to this fetchUsers, what we would do
is wrap this all in a try catch block.
So we could do try all of this.
Try all of this.
And if that works, great.
But otherwise, catch.
Or in other words, if an error happens in these three lines,
don't halt the program and don't immediately crash with an error.
Actually allow me to do something with that error.
And so what are we going to do down here?
Well, we can do whatever we want.
Maybe we're just going to console.error this error and call it a day.
And so now, if we call fetchUsers with error handling,
even though there's no error, if there were an error, this would catch it.
And so now let's actually go back to that contacts app
that we've been writing over the past few weeks.
Up to this point, the way that we're generating contacts
is by invoking that function that generated random contacts.
So if you remember from past weeks, we had this file called contacts.js.
And within it, we hardcoded a bunch of names.
And at the very bottom, we exported an array
that just created a bunch of random contacts.
But now rather than doing that within the app itself,
we're actually going to hit an API for all that information.
And so let's go ahead and use that API that we've been exploring thus far
and generate those random users from their API
rather than doing it within our app.
And so in order to do that, we're going to have to do some data fetching.
And so where might we want to do that?
So currently, the place that we're doing all this is in app.js.
And so if you look at the top of app.js, you
see us import contacts from contacts.
And that goes ahead and grabs all of those random contacts
that we created and imports them into this app.
And at the bottom of the file, we see in our class app,
we initialise the state to be whatever those contacts were.
But now, rather than using those contacts, let's start with no contacts.
And let's actually go ahead and fetch those contacts.
And so these contacts, rather than being generated from within the app,
are going to be fetched from that API.
So let's first do it with promises, just to practice.
And so where might we want to do this?
So we want our application to go ahead and fetch all of these random contacts
as soon as it starts.
But we don't want to hold up the application as it's fetching.
And so how might we hook into this application
and get it to send a request when it starts?
Does anybody remember what we could use to do that?
Yeah?
AUDIENCE: componentDidMount.
SPEAKER 1: Yeah, we can use componentDidMount,
one of the React lifecycle hooks.
And so we can do componentDidMount.
And within componentDidMount, we're going to want to send out that request.
And what does that request look like?
Well, it looks something like this.
Not exactly like this, but it's a good starting spot.
So we're going to fetch from this URL.
Then we're going to extract the JSON out of that response,
and then we're going to console.log that result.
But rather than console.logging that result, what should we actually
do with the result?
And let's rename result to contacts.
Actually, let's first remember exactly what this looks like.
And so if go ahead and execute that, we get back
this object with the results key.
Well, that results key has a bunch of random users.
And so here we know that contacts is not actually the contacts that we want.
It's a response object, or results object.
But what do we actually care about?
We really care about only that results key.
And so let's go ahead and do this.setState
and set our contacts to be equal to that results dot this key called results.
And since we only really care about one key in this object,
we can go ahead and use that object destructuring.
So we can say we only care about the results key of this object,
and assign a variable called results to that.
And so now we have some shorthand to do exactly what we want.
So if we go ahead and save this, we can watch what happens.
So let me log in.
Uh-oh, undefined is not an object.
So what the heck is going on here?
I thought that we were doing what we wanted.
So let's actually first console.log what we're getting.
So before we go ahead and set state.
Let's console.log those results.
So when we log in, we see a bunch of things.
It's working.
Our request is sent.
And what does this look like?
Well, we see there's an object, there's a cell, there's a date of birth,
an email, gender, ID, location, log in.
There's a bunch of stuff that we cared about.
But we get this red screen, called undefined is not an object.
What could be happening here?
Well, we have a small hint, evaluating contact on name 0.2 uppercase.
Where are we doing that?
Well, we're actually-- we're assuming that the data that comes back
is the same as the data that we had in our original application,
but that's not necessarily true.
So let's look at the row that we defined earlier.
In this, we assumed that each person that came down
had a key with the name in it and a key called phone.
But that's not necessarily true with our new objects.
The phone is still called phone.
But name is actually now an object, where we have first, last, and title,
and so props.name might not necessarily exist.
So let's actually push this problem to a little bit
later and forge ahead with the state of fetching that we're doing.
So we know that the data fetching is working.
And we're doing it in componentDidMount here.
For right now, we're using this promise, this .then, .then notation.
And we can actually modernize a little bit by using this async/await.
So how might we want to do that?
Well, in order to get from promised to async/await, all we need to do
is await this and assign it to some variable.
So if we did const repsonse equals this, now response
is going to wait for the fetch to come back and wait for that promise
to fulfill and then set itself as a variable.
Then, what did we want to do with that response?
Well, we wanted to do const--
we'll just call it results right now equals that response.json.
See how that's exactly the same as what we were doing before?
But if you, again, remember, response.json actually
returns of promise.
And so what are we going to do with that?
Well, we need to wait for that promise to fulfill, utnil we can do await.
Or in other words, just wait for that response.json promise to be done.
And then, assign that variable, the result, to this variable
called results.
So that does this.
Lastly what, do we want to do?
Well, we actually didn't care about that object--
that entire object.
We only cared about the key called results.
So we can go ahead and do this destructuring here.
So we say whatever object comes back from response.json after the promise is
fulfilled, we just get the key called results and assign
this variable called results to that.
And lastly, what did we do?
Well, we wanted to await--
we wanted to console.log that.
So again, the same thing here.
And then, go ahead and set the state as so.
So we're all good, right?
This is good to go.
Let's go right.
Hm, syntax error.
Await is a reserved word.
What is going wrong here?
Well, if you remember, the only time you can use
await is within a async function.
And componentDidNetMount is not an async function.
So how are we going to go about and fix that?
Well, we can just create a new method called--
well, one quick way is to do this.
We can create an anonymous function here.
So we can say--
if you remember back to an early, early lecture
when we talked about IIFEs, of immediately-invoked function
expressions, we can just create an anonymous function here.
So we could have an async function that does this
and then immediately invoke it, so again,
an immediately-invoked function expression.
And so we have an anonymous, asynchronous function here
that does all these things.
And then, we wrap it in parentheses and immediately invoke it.
And that will immediately invoke that asynchronous function.
And so if we go ahead and save that, then everything--
we need to use an asynchronous arrow function.
Just because if we use a normal function here,
this binding down here does not make sense in the eventual state of the app.
And so by using an arrow function where we bind the this to be the this,
lexically here, like we want.
And again, it all works, and we get that same error that we're seeing before.
But this is a little bit messy and hard to read.
And so what is a better way to structure this such that it's
much more easy to maintain and read?
Well, we can abstract all this out into its own function.
So we can just delete all of this.
Actually, let's just go back a few steps until we are here.
So this is where we were, and this is what we want.
But let's say rather than calling this componentDidMount,
let's call this fetchUsers.
So now, we have a method called fetchUsers,
and it's actually a synchronous method.
But we want to set it as an arrow function so that where sure
that the-- this binding works.
And then now, we just have to call fetchUsers.
And so we're going to call that.
Well, we can do that here.
We can do componentDidMount and then immediately call fetchUsers.
And if we do that--
oop, fetchUsers doesn't exist because I mean to do this.fetchUsers.
If we go ahead and do that, then, again, we
get the same error, because it worked.
And so now let's go ahead and fix this error.
And so if you want to go ahead and read up about async and await,
you can go ahead and follow those URLs.
So let's talk about transforming data little bit.
So something got messed up.
The shape of the data that was returned from the API wasn't ideal.
It wasn't the data that we were supposed-- that we thought it should be
or that we originally wrote the app to be.
And so where might we want to do this transformation?
We can do it every single time we render a new row.
So every single time we show a user's contact information, we can go ahead
and say, oh, we know that the data that we got back from the API endpoint
was not what we really wanted to.
So let's go ahead and make sure that what we're actually showing
is from the correct keys in the new API.
But that doesn't seem very good, right?
Every single time we re-render, we're going
to have to redo that transformation.
And so where might be a better way to do that?
What if we did it right when that data came back?
That's good in a few ways.
One, it's more efficient.
We only have to do it once.
So as soon as the data comes back, we go ahead
and transform the data to make it look like what we want it to look like.
And then, we're done.
Every single time the app re-renders, we already have the data that we want.
But it also gives us this thing called an abstraction barrier.
So we've talked about abstractions a little bit
and how, by creating an abstraction, what
we're doing is we're taking some aspect of a problem
and breaking it off into its own small problem.
But what is an abstraction barrier?
It means you don't really care what this abstraction is.
You only care what is outputted at the very end.
And so by transforming the data outside of the component that cares about it,
that component really never cares about how we got that data
or where we got that data or how it was transformed.
It only cares about what the data looks like when it receives that data.
And so if we go ahead and do all this transformation
as soon as we get that data, then the rest of our application
doesn't really care what the API looks like.
The API might change.
And maybe we'll change the way that we've transformed the data in response
to that.
But the rest of our API-- the rest of our application doesn't care.
There's an abstraction barrier between our application and that API.
And so how might we do that?
Well, right now fetchUsers is something that we created within our app.js.
But our app.js doesn't really care where we're getting all these users from.
So if this was randomuser.me or if this were randomuser.com or randomusers.og
or anything like that, our application really doesn't care.
It only cares that we're getting those users from somewhere.
And so we might want to take all of this logic and move it somewhere else.
And where might we want to move it?
Well, let's go ahead and create this new file called API.js and open it up.
And so now, we've gone ahead and created a separate file, just
like the rest of our application.
When we have a component that isn't necessarily--
needs to be defined within a particular other component,
we can go ahead and create a new file for that component and abstract it out.
We can also do that with our API logic.
So our app.s doesn't really care where or how we're getting all of this--
these users.
It only cares that it's getting it.
And so maybe we can take that logic and move into this file called API.js.
And so let's take this fetchUsers, take it out of app.js,
and instead, put it in our API folder-- or API file.
So we have this thing called fetchUsers.
Let's export it.
So we're exploiting this const called fetchUsers.
And what it is, is it's an async function that will await this fetch.
It will take the results, console log the results,
and then do this this.setState results.
That no longer really makes sense in what we're doing.
this.setState really only makes sense in the context of a stateful component.
And so what should we do instead?
Maybe we should just return results.
And so now what this function called fetchUsers is doing
is going ahead and doing that request.
It's getting the json back.
And then, it's returning it.
And so now, in our app.js, we can actually--
we can get rid of that.
We can do import fetchUsers from this file called API.js.
And now, where are we going to use this fetchUsers function?
Well, in componentDidMount.
So now, it's no longer this.fetchUsers.
Now, what we all want to do is fetchUsers.
And this returns results.
We could either do .then use these results and set the state with it.
Or we can, again, use that async/await keyword.
So again, if we want to use async/await, we have to define another method here.
So we'll go ahead and do async fetchUsers is
a function that awaits fetchUsers.
Probably not ideal to use the same variable name here.
So let's just call this getUsers and then this.setState, those results.
So if we wanted this to work, we should have done this.
So again, this line and these two lines are functionally the same.
But if we do want to stick with that async/await,
we can just create this new function, an async function called getUsers-- oops,
should actually be getUsers is an async function here.
We can define a class property, called getUsers, which is an async function,
which awaits fetchUsers-- which we defined in a separate file within
our API.js--
grabs those results, and then just sets the state
so that the contacts are those results.
And then in componentDidMount, we could just invoke that getUsers.
So we're back, basically, to the point that we were a minute ago.
We still have that same error, because the API, the shape of the data from API
is not in the shape of the data that we originally wrote our app for,
which has been a problem thus far.
But the data fetching is still working.
And additionally, our app.js function, or app.js file, looks a lot better.
So here, nothing within the app should really
care about where we're getting about data--
nothing in this class file, I should say.
All it cares about is that we're getting the data.
And so by abstracting out this fetchUsers into API.js,
we have, now, a nice abstraction, where if we
wanted to change the API that we're getting it from,
this class should not really care about it.
Really, the only thing that cares-- tat should care about is that API file.
And we've gone ahead and moved all of the logic there.
And so now, let's try to get that--
the shape of the data to match the shape that we're looking for.
And so what is the shape that we're looking for?
So if we look back into contacts.js from a few weeks ago,
remember that a person has a name and a phone number.
And that's it.
There's no such thing as a first name and a last name.
There's no date of birth, no nationality.
We only care about the fact that they have a name and some phone number.
And so let's go ahead and get the data from the API to look like the data
from--
that our application is expecting.
So first, let's go into our API file.
So we have this here.
What exactly does a user look like?
Well, it's an object with a cell, short for cell phone; date of birth;
email; all of these keys.
And what are the only two that we care about?
We care about name, which, here, is an object.
And we care about phone, which already exists.
And so let's write a basic transformation function,
so a function that takes data and transforms
it to look like new data, that takes an old contact,
or a contact that's returned from this API, and makes it look like a contact
that we're expecting.
And so let's create this function called processUser--
or Contact, which takes a contact and returns a new object.
And what does this object look like?
Well, it should have a name, and it should have a phone--
both strings.
And what did a name and phone look like in the object from the API?
Well, phone is just what we expect it to look like already.
And so we can say the phone is just the contact.phone.
But the name looks a lot different.
So in our application thus far, a name was just
a continuous string, where it was their first name, a space,
and their last name.
But in this API, a name is actually an object with a key called first,
a key called last, and a key called title.
So how are you going to get this to look like what we want it to?
Well, we can use those template literals.
So if you remember, we can do backticks, which
allows us to create a string literal and within it,
evaluate JavaScript expressions.
And so maybe we want to have something called like a firstName, a space,
and then some value called lastName, and call that a full name.
Unfortunately, firstName and lastName don't exist.
But we can actually get them.
We can do that contact.name.first.
And that will get us their first name.
And the same thing is true with their last name.
And so if we wanted to transform that name object into what we're looking
for, we can just do contact.name.first.
And then here, we can do contact.name.last.
And now we have what should, in theory, be a name.
We have their first name, separated with a space, and then the last name.
And since we're wrapping it in backticks,
these JavaScript expressions that are surrounded by this dollar and curly
brace are just evaluated and then cast into a string.
And then, what do we do for phone?
Well, we just grab the contact out of phone,
because that was already matching exactly what we were looking for.
And so now, we created this function called
processContact that takes a contact and turns it into what we're looking for.
But how do we apply that to this fetchUsers?
Right now, we have the results.
And how might we, say, go through that results array,
and for every single value in that array, transform it?
Well, we've talked about a function that does a very similar thing.
It goes through an array.
And for every single value, it passes that value into a function
and creates a new array, where every single value is
the output of that function.
So rather than just returning the results, what we can do
is we can map over those results.
And what do we want to do for each of those results?
We want to take that contact and then pass it into processContact.
Or in other words, we just want to process the contact.
What does that mean?
Well, we can do a .map.
So for every single value in this results array,
we're going to invoke this function on that value.
And what does this function do?
Well, it's expecting a contact.
And then, it just outputs the contact that we're expecting.
And so does that fix our problem?
Yes, it does.
So now, we see a bunch of things.
We see first name, space, last name, and we see their phone numbers.
So do you see why doing this transformation right now
is much better than doing it within the application itself?
Since we already wrote the application, we
don't know exactly where we use name every single time.
It might be in the row, and it might be also done 10 other times in the app.
And if we wanted to say, move, what used to be name as a string
into name as an object with a first and last key,
we're going to have to go back and change that
every single time in our code.
But by doing this here, we just have to do it once.
And now, it creates this abstraction barrier.
So now, our application doesn't really care how random user.me
wants it to express the contact.
And maybe they're going to change that down the line, and all we need to do
is update this transformation such that we take the contact from however
they wanted to express it and just transformed it to look
like exactly what we're expecting.
And this is much more efficient, because we just do it once.
We do it as soon as we get all of the users
rather than having to do it every single time the application is re-rendered,
every single time that we use this.name.
So let's go ahead and take a break there.
And then after this break, we'll forge ahead
to finish implementing our Contacts app.
Hello, and welcome back.
So before the break, we were writing our Contacts app.
And we got up to this point, where we have a bunch of contacts
that we're fetching from an API off running in the cloud somewhere,
and we're displaying those as users in our application here.
And so this Contacts list is actually a bunch
of people who don't live in our app.
They're actually hosted on some cloud which,
we're getting all of these users using the API, the public API running
in the internet.
But we have one aspect of our application
that's a little bit insecure.
We have this page that says you're currently logged out.
And when click the Press to Log In button, we're immediately logged in.
This isn't the most secure application ever if the way that you log in
is just to click.
And so let's actually go ahead and implement a login
screen using a real API.
So if you look on the website at the code that is linked for this lecture,
you'll see that there's this thing called an-- this authServer.
And in this authServer is a very, very simple server that I wrote.
And if you look at the README, it's a mock authentication server.
You can post-- ignore post for now-- to any end point, and it will act as a log
in.
There's only a single user name, a single user.
Their username is username, and their password is password.
And there's no way to add new users.
But we can go ahead and use this server as an API.
And so if you look at the installation instructions,
the way to run it is by doing npm install.
And then after the installation is done, we can go ahead and run npm start.
And now, we have an error, because something is running somewhere.
Here.
So if we go ahead and run npm start, it goes ahead, starts the server.
And now, it's running.
It's listening at http localhost 8000.
And if you go ahead and visit it, we can see that it's running--
currently returning not found.
And we'll see why in a second.
Let's go ahead and write--
add some authentication to our application.
So what is authentication?
Well, it's a process to determine if a user is who they actually say they are.
Because unlike in the real world when I'm talking to somebody
and I can immediately see who they are, the internet
doesn't really work the same way.
If you're an API and you're just getting your request out of nowhere,
how can you really confirm that the request came from who they said it was?
The way that we usually do this is by using a name and a password.
Presumably, only the person with the name knows the password that they sent.
But how do we actually send requests with a name and a password?
So the way that we configure requests in this Random User Generator
is by adding it to the URL.
And so if we wanted, say, 5,000 results, we just visited this URL here,
called https://theirapi?results5000.
But for authentication, that seems a little bit weird.
Would we really want to do something like https:// some URL?username=jordan
password equals my password.
That doesn't sound very smart, right?
Imagine somebody was using my computer after me.
They want to visit that URL.
And all of a sudden, my browser auto-completed the whole thing
that said, oh, user equals Jordan.
Password equals my password.
Now all of a sudden, I'm compromised, and everybody knows my password.
So this probably isn't the greatest way to transmit secure information.
What might be a better way to do it?
Well, it turns out there are these things HTTP methods.
And one that we commonly use is GET.
This is the default for browsers.
This is the default for fetch as well.
So if I just go to randomuser.me/documentation and load
the page, that sends a GET request to this.
And I can confirm that by looking at my network traffic.
If I refresh the page, I see a bunch of things happen.
And if I inspect this request, I can see that the request method is GET.
And what is the URL that I'm sending it to?
Well, it's this request URL that I have typed in my address bar.
So if I just go to this URL and hit Enter,
browsers will use a default request method of GET.
And the Same thing is true with fetch.
If I just go ahead and type fetch this URL,
it goes ahead and sends a GET request.
And how do we add parameters to the GET request?
Well, we do that by appending a question mark,
unchaining key value pairs by doing key equals value.
And we separate them by this ampersand.
And so if you remember earlier when we were talking about this API,
we see a question mark here, which means here comes some additional parameters.
And you see key value pairs separated by equal.
And so the key here is results.
And how many do we want?
Well, we want 5,000.
And down here, we could also have a separate parameter,
called gender or female.
And what if we wanted both?
I waved my hand at it a little bit earlier.
But what I did was I said, results=5000%--
another key value pair.
And if I wanted to chain a bunch more, I could do
results=5000&gender=female&nationality, or nat, =us.
And so just to demonstrate real quick, I can do nat=us and results--
is it called results?
This is called results=10.
And also, I only want gender=femals as well.
And if I go ahead and send that request, I get back what looks like 10 results.
And every single person is--
should be female.
So one, two, three, four, five, six, seven, eight, nine, and 10.
And so I can pass additional parameters by just doing
key equals value after a question mark and sending the ands.
And how do I know which key value pairs exist?
Well, since I'm a consumer of the API, I'm
at the will of whoever wrote the documentation.
And so to know exactly what key value pairs are available,
I just have to read the documentation for that particular API.
But that, again, doesn't solve our problem.
If I wanted to authenticate through some server,
I don't want to be sending user=Jordan and password= password.
So what might we do instead?
Well, it turns out there are other HTTP methods.
And one is called post.
What is posting?
Well, it's like submitting data or like a form or something to an endpoint.
And the origin of this word post is actually
from posting things to a billboard.
And so if you wanted to add something, so add some new data to a billboard,
you just post it up to the billboard.
And so that's how this HTTP method, or this verb, came around.
And so by posting to an end point, we can actually submit data.
And where do the parameters go, if not in the URL?
Well, turns out there's actually a request body
in this thing called a post.
And so GET requests don't have a body.
And so the only way to add parameters is through the URL.
But in a post, you can actually put it into the request
itself so that it's not visible in the URL, which is good.
If I wanted to now send a request to an authentication end point,
I don't have to put my parameters in the URL.
I can now put them inside their requests body where people who then go
visit the page after me don't see it auto-correcting in my browser.
How do I do that?
Well, I'm probably going to use this thing called JavaScript Object
Notation, or JSON.
And so by what I do is I post some JSON and I
add this header called content-type application JSON.
And I'll wave my hand a bit at headers.
But basically, what this is saying is, hey,
API, the stuff that's coming in this request
is JSON, or JavaScript Object Notation.
And so my body has to then be a JSON on string.
And so now, let's actually go ahead and start using this.
So let's take a look at our server.
Well, first, let's run it.
So it's currently running at localhost:8000.
And if you remember, how do I actually use this authentication server?
Well, I can post to any end point, and it will act as a log in.
And so now, we know what post means.
Posting is opposite of GET.
So rather than just visiting localhost:8000 in my browser,
which is what I did, that does what?
It sends a GET request.
And so that's why it's saying not found.
Because it doesn't know what to do with that GET request.
It only knows what to do with posts.
And so now we can actually post to this and try to log in.
And so how the heck are we going to send a post?
Well, it's the same way that we sent our get requests.
So let's head over back to this console.
And let's send a post request to our API.
So let's do fetch http://localhost:8000.
And let's just do that and see what happens.
Well, we get not found.
We try to get it.
We see this thing called 404.
We don't know what that means yet, but not found.
Why is that not found?
Well, it's because we know that we want to post to that end point.
Actually, I have a feeling this demo's going to fail.
All right, we're going to forge ahead anyway.
And this is probably going to fail.
But let's go ahead and send a fetch request to that same API.
And let's add some additional configuration.
And so if we want to send a post request, what
we need to do is pass some additional configuration that says,
what method do we want to use?
Well, we're going to use a method called post.
And we'll go ahead and send it.
And we can go ahead and look in our network.
We can see that we sent this request.
What was the URL?
Well, it's localhost:8000.
And what is the method?
It's post, as opposed to the one that we had sent a few seconds earlier,
which is a GET.
And if you look at the status code, it's saying 404--
not found and 400-- bad request.
We don't really know what that means, but let's forge ahead and go ahead
and try to implement this login screen.
So what does the login screen look like right now?
Well, it just says you're currently logged out.
And we press this button.
And all of a sudden, we're logged in.
Again, not very secure.
And so let's go ahead and use our authentication server
to make sure the user is who they say they are before letting them in.
So where is this logic?
Where does this logic live?
Well, if we want to find it, we can open up app.js, scroll down.
We'll see that we have an app navigator.
And what is the app navigator?
Well, it's a switch navigator, if you remember back
to last lecture, that is the navigator where it's either
showing one screen or the other.
And when you switch in between, they're actually unmounted.
And so there are two screens on this switch navigator.
There's the login screen and the main screen.
And so we want to actually be working in the login screen.
And so let's go ahead and find where that login screen is implemented.
So if I just search for the login screen, I see it's used there,
and actually import it in from this URL, ./screen/loginscreen.
And so now I know that if I want to affect that page,
I need to go edit that file.
And so let's cd into screens and look at that login screen.
And here, we have a page.
We see export default. This class is called login screen.
It extends react.component.
It has a class property, which is a function called login.
And what does it do?
Well, it just navigates to the main screen-- again, not very secure.
And then, we see that it's rendering some things.
It's rendering a text, element that says you are currently logged out,
which is exactly what we saw.
And we saw this button that says Press to Log In.
And on press, it just invokes this function called Log In.
And if you've never seen this before, there's a convention that says,
for methods that are only used within a particular class,
use an underscore before that method.
The community is a little bit torn on whether to do that or not.
It looks like Eric and Brent like using the underscore log in.
But again, about half the community also does not--
prefers not to use that login, the underscore, because in method--
in other languages, it might mean this is a private method.
But since private methods don't exist in JavaScript,
we don't want to confuse people to think that it's a private method.
So the community is a little bit torn.
Brent and Eric like to use it.
I will not use it.
But I will leave it as is just in their honor, I guess.
Let's actually make this more secure.
So we don't want to just, by default, log anybody in.
And so first, we actually want to go ahead and add a couple of text inputs
that controls the username and password that we're going to send.
And so let's get rid of this text that says you're currently logged out
and instead, put some text inputs in.
So we want one text input that is going to be used for the user name,
and we want one for the password.
What are we going to do when somebody starts typing their username?
Well, we should probably update the value.
And what is that value?
Let's have it be this.state.username
And how do we change that?
Well, on changed text, we're going to call this.handleusernameupdate,
which doesn't exist yet.
So first, let's declare this state where it
starts with a username of empty string and a password of empty string.
And then, let's also declare onChangeText--
or handleUsernameUpdate, which takes a username
and will update the state when it changes.
So let's do this.setState that username.
And if you don't remember what this shorthand is, it's basically this.
But rather than using the key and the value as the same string,
we can actually use the ES6 shorthand, like that.
And let's create the same thing for password.
And go ahead and make sure that this text input is showing what it should.
And so the value here should be that password,
and the onChange text should be the way that we handle that password.
Cool.
So now, we have this login screen.
And so you see there's a user-- it's expecting a username and password.
And we can type in the username here.
We can type in the password here.
And if we press to log in, we're automatically logged in.
If we type no username and no password, we're also
logged in, which seems kind of weird.
Why is that happening?
Well, if you remember what happens when you press that button,
we call this login function.
And that login function disregards anything that we've typed
and just navigates us to that main people.
So let's actually use those things.
And so let's remember how we use that API for our login server.
So let's just cut and paste that here.
So we're going to have to fetch from this API.
And if you remember from that README, it's expecting a post to any endpoint,
so I can put slash whatever.
It doesn't matter.
We're going to use the method post, and we
need to include, somehow, the username and password that we want.
How are we going to do that?
Well, if you remember back to the slides,
if we want to post some JSON on, which we do,
we're going to have to have content-type application JSON on the header.
And we must include a body that is a JSON string.
So let's do that.
So we have method post.
We also want to include some headers.
The only one we care about is this thing called content-type,
and it should be application/JSON.
And again, that's just telling the server, hey, here comes some JSONs.
And where's the JSON going to be?
Well, it's going to be in the body.
I only know these configuration keys because I read the documentation
on fetch.
And so in order to put something in the request body, we have some--
an object here.
What do want in object?
We want the username, and we want the password.
What are the values for the username?
Well, the username is gonna be this.state.username.
And the password is going to be this.state.password.
But this isn't JSON string.
This is just a JavaScript object.
And so if we want to make this a JSON string,
there is this handy function called JSON.stringify,
which takes a JavaScript object and turns it into a JSON string.
So let's go ahead and send that fetch.
And then, I'm just gonna use a promise here, because it's faster for me
to type.
All right, so I'm gonna take the result from that and console log it.
So if I press to log in, it's going to log me in,
but presumably, it's going to console log something.
And it's way down there.
So let me just clear all these logs and start over.
So I'm going to press to log in, and I see this response.
And what is in the response?
A bunch of things.
And we see this thing called OK False and Status 400, Status Text Undefined
Type default. Did it work?
It's kind of hard to tell.
All we're getting back is this thing, a number.
And what the heck does that mean?
Well, it turns out that's not an arbitrary number.
That number is actually part of a standard.
It's an HTTP request code.
So every single network response actually has a code associated with it.
And these codes, they mean something.
It tells you exactly whether or not this response or this request
was successful.
And so if everything went great, we get a 200.
So 200 just means OK.
It worked.
Good job.
But not everything's a 200.
We see here that this particular one that we sent back got a status of 400.
What is a 400?
Well, that means a bad request.
Or in other words, the request that you sent
is missing some things that we wanted.
Or maybe it wasn't the shape that we wanted it to be.
And it turns out there are other-- there are other numbers that this could be.
You could also get a 403 Forbidden, which
means you're not allowed to access this endpoint,
or you did not have the correct permissions
or the correct authentication in order to access this endpoint.
There's also a 404 Not Found, which is this endpoint does not actually exist.
So if you are browsing the internet, odds are you've hit a 404 page before.
GitHub has a pretty nice one.
So if you go to github.com/ some gibberish string,
you get back this Not Found page.
And oftentimes, it's styled so that it's saying,
maybe not everybody knows what a 404 is.
And so we'll tell them, this is not the web page that you're looking for.
This page doesn't actually exist.
And so a lot of websites will choose to have some friendlier 404 pages,
unlike our localhost:8000, which also just returns that Not Found.
And if we actually inspect the network and visit localhost8000,
we can see that we got a status code of 404 Not Found.
What other HTTP response codes are there?
Well, one that you might see is 500 Internal Server,
Error which means the error was actually not on your part.
There was an error on the server side code.
And I'm just letting you know that this is not respect-- this didn't work.
I errored out, but I'm not going to keep you waiting.
Of course, there's a bunch of other ones.
There's like 401, 402, 418.
I'm a teapot.
This was actually a joke created by the people who made the HTTP response codes
list.
It was an April fools joke.
This response code is only returned if you send
a request to make coffee to a teapot.
The teapot should return 418, I'm not a coffee maker.
I'm a teapot.
And if you want to look at all of the response codes,
you can go look at the documentation at this link.
And so now, we know what this 400 actually meant.
When we sent some stuff to the login, we got back 400,
because this was not what we wanted.
There was no username or password that we included in that fetch.
And so if we refresh and actually type the username and a password,
press Log In, the status changed.
It's no longer a 400.
It's actually a 403.
And what does a 403 mean?
Well, 403 is forbidden.
Or in other words, those were not the correct credentials.
So let's actually, since we have a little bit of extra time before diving
into the React Native code, let's look at what that server-side code actually
looks like.
So if you're actually interested in on-- in what
this authServer code looks like, it's actually also written in JavaScript.
If you remember back from an earlier lecture,
we talked about this thing called node.js,
which allows JavaScript to write-- to run on a server,
even though it's a language that was originally
intended to run only in the browsers.
And so if we want to look at this, we can see what is actually backend code.
And so I'll wave my hand at a lot of this express stuff,
app.usebodyparts [? to ?] .JSON, which is kind of self-explanatory.
It just means if there's JSON in the body, go ahead and parse it.
But we see this thing called app.post.
If you remember post, that means for any requests
coming in at this particular endpoint-- and what endpoint do we care about?
Star, which means every single endpoint.
So if you post to any endpoint, we're going
to run this function that takes a request and a response.
And what does it do?
Well, it extracts the username and password, so familiar syntax
of which we discussed earlier, which means look at this rec.body.
And so if you remember, our fetch request had this key called body.
And so now, since we've embedded stuff into the body,
now the backend server is actually looking at the body that we requested.
So again, the front end and back end code are communicating with each other
by using these familiar constructs.
So rec.body's an object.
It's the JavaScript object that we run on the front end.
And it had two keys, if you remember them.
It had a username and password.
If you remember this shorthand, it means create
a couple of variables, called username and password,
and assign them to the username and password
respectively, keys from rec.body.
And so we're taking those username and password.
If neither one exists, return a result status code of 400
and send a string along with it, called missing username or password.
Otherwise, if there is no users in our username-- say, oh, 403,
that this user doesn't exist.
And if the username's password doesn't match the password,
Return 403, oh, this is an incorrect password.
And if you're curious how we're storing these username/password combos,
we're just using an object.
So we have declared, at the top, [? constant ?] users,
where the user called username.
Their password is password.
And so in this particular way of implementation,
we have a users, where the username are the keys,
and the passwords are the values.
And so the only user we have in our system is a user with the username
called username, and he's--
with a password of password.
And so we're just going to continuously check against that.
And so again, super simple--
if they post to any endpoint, grab their username password.
If they don't include a username or password, that's a bad request.
That's a request that's not-- that doesn't
have the things that we're looking for.
And so go ahead and tell them, oh, this is a status code for 400.
Send this string.
And it's up to whoever wrote the backend to know all of the status codes
and use the appropriate one.
That's not guaranteed.
But generally, people are using the correct ones.
Then, we go ahead and see if that user exists
by checking in that user's object.
Again, in practice, this might be revealing too much information.
If you imagine somebody can make a request that says, oh,
does user A exist?
Oh, no, I got 403.
It doesn't exist.
Does a user called B exist?
How about C, D, E, F?
And they can go ahead and brute force every single possible string
and figure out all of our users.
So if this were your actual implementation,
attacker could actually probe the user and find all of your usernames.
But in practice, a server may have protections,
like block an IP if they send too many requests, and stuff like that.
But then, if that user does exist, but the key in that user's object,
which we are storing the password, is not
equal password, which is what they submitted,
then say, oh, this was the incorrect password, again,
with that 403 Forbidden code.
Otherwise, they are who they say they were.
Let's go ahead log them in.
Send 200.
And if you remember what 200 means, it's just OK, it worked.
And then down here, you can see catch 404.
So if it's not a post to anything, return a 404.
Or create an error that's called Not Found.
Set the status to 404.
And then go ahead, and down here, send that status.
And I'll wave my hand at the rest.
But basically, this web server takes a username and password
if you post an endpoint, checks to see if it works.
If it's good, it returns 200.
Otherwise, it returns the appropriate status code.
So there is a peek at some backend code to see exactly how our server works.
And so as expected, if you just visit localhost:8000 at a browser,
if you remember, if we just visit some URL to browser,
what type of request is it sending?
It's sending a GET request.
And so, it is returning that 404 Not Found.
And if we post with a username and a body,
we get a 403, 3 because it was an incorrect password.
And finally, if we post with no username and no password,
we're going to get that 400.
So let's actually implement that page.
So currently, our login is just sending a fetch request where the body is this
.state.username and the password is this .state.password and then ignoring
whatever comes back.
And we're just navigating to main.
So let's actually write something that makes sense.
So rather than making this a synchronous login, let's actually make it async.
And now, let's do the result of the response from this call--
is what happens after you wait for the fetch to come back.
So now, we have this.
So we have-- we're fetching to this URL.
We're sending a post.
We're telling the server, hey, by the way, some JSON is coming.
Here it is.
It's a object with a username, where the username is this.state.username
and password is this.state.password.
here you go.
And now, we can go ahead and say if response.status=400--
because if you see, we have your response.status if it's 400
or if it's 401 or if it's 402 or if it's 403.
And I need to count every single possible bad status code in order
to know that, indeed, this is a good status.
This seems really annoying.
You can also use some greater than or equal to 400 and whatever.
But it turns out there's actually something implemented for you.
If you look at this object, you see this thing called OK False.
And if you look at the documentation for the response object,
it actually does that for you.
It does this-- if it's 400 or 401 or 402 or 403 or anything up to 500,
it makes that check for you.
And it basically exposes it to you as this key called OK.
And so rather than checking 401, 402, all of these,
you can just check response.ok.
Then, they're good.
Or in other words, if the response is equal to 200,
because we actually know in the backend for this particular example,
it'll only return 200.
But we can go ahead and use this more general one.
Then, we're going to want to log them in.
And we can go ahead and return, because that was successful.
Otherwise, there is a problem.
They did not do what they wanted to do.
Or their password was not correct, or they were not actually a user.
So we're going to have to look at the text that came back.
Because if you remember from the backend,
we're actually getting some text back.
It's saying, hey, this is a bad password,
or hey, this user does not exist.
And so just like we did this thing called response.json
which takes that-- which extracts the JSON from the quest,
there's also this thing called response.text.
And so since we know that we're not receiving back an object,
we're only receiving back text, we can await this.
And again, that returns a promise.
And so now, if the response was not OK, we know that there is an error.
I mean, what was the error?
Well, we can just see what the backend is returning in that text.
And so by doing response.text, that returns a promise with the text.
And we can await that to get our error message.
Then, what should we do?
Well, maybe let's do this.setState, and some error message is that.
So now, we have a pretty good thing going.
This login is now an asynchronous function, or a async function.
What is the first thing you do?
Well, we send this request to localhost:8000.
We know that we're supposed to be posting some JSON,
because that's what the documentation said.
And we're waiting for it to come back.
When it comes back, we set the response to be this response variable.
If it was OK, if we got a 200 back, then we're all good.
We can go ahead and log them in.
And the way we log them in, in this case,
is just navigating to the main screen.
Otherwise, something went wrong.
And let's figure out what went wrong.
So let's extract from the response the text that came back.
And so if you look at the documentation for the response object,
you see that text is a method on there.
And it returns a promise.
And so we can await the response of that and extract the error message
from that response.
Then, what do we do?
Well, maybe we should tell the user that something went wrong.
And so let's go ahead and put that in the state.
So now we have an error called error message.
And what are we going to do with that?
Well, let's show it to the user.
Or maybe let's show it to them in some red text.
So now, hopefully, we've just written some code
that will send a request to log in with whatever username and password
the user has typed in.
If it's good, we'll log them in.
And if it's not, we're going to put an error message into the state.
The error message corresponds with whatever response we get back.
And then, go ahead and show it to the user in render.
So let's see if that works.
Oop, can't find [INAUDIBLE] text, because I didn't import it.
And now, if we press to log in, oh, missing username or password.
It's not red.
style.error-- oh, now, it should be red.
And there we go.
It's missing a username or password, which
is good, because we didn't type in a username or password.
Let's see if this works if we type in a username.
Well, we're still missing a username or password, which is true.
We're missing the password.
And now, that user doesn't exist.
Because what does exist?
Well, a user called username, and it's not a capital username.
It's a lowercase username.
Now, if we press to log in, oh, we're getting
incorrect password, which is good.
Now, it's actually showing some input to the user.
But it might be annoying for users who have all lowercase usernames
to have to manually go back and do a lowercase U.
And it might also be bad for passwords to be shown.
But let's just verify that this works.
If we press to log in, lo and behold, we're
logged in, because we submitted the correct password.
So yay, our auth is working, but it might not
be the greatest user experience.
Because we know that our only user is all lowercase.
But by default, the keyboard is always capitalized.
And so if the user wants to type in their username,
they're always going to have to make sure to uncapitalize that first.
There should be a way to do that, though, right?
You'd think that with a text input, the people who
wrote React Native should know like, oh, maybe
not everybody wants to capitalize all of the words.
So how are we going to see if that does exist?
Well, let's actually look at the API documentation
and look at the API for the React Native TextInput.
So if we open up the documentation for TextInput, we can see all of the props
that we can pass to this this component.
Or in other words, we can see the API for this component.
And maybe one of them will allow us to not
automatically capitalize everything.
So if I Command-F for capitalize, oh, looks like there's
this thing called autoCapitalize.
And this tells the text input to automatically capitalize
certain characters.
And we can either pass in none, sentences, words, or characters,
and the default is sentences.
So the first letter of each sentence is, by default, capitalized,
but we don't want that.
So let's actually use this prop called autoCapitalize
and pass in none to get what we're looking for.
So here, we have a text input, and we can say autoCapitalize equals none.
So now, look, it's not automatically capitalizing.
And so a user now can just type in their username
without having to worry about this auto-capitalization.
We still have a bit of a problem.
We don't want the password to just be shown in plain text.
And so maybe there is a way to do that as well.
So again, let's look at the API.
A lot of stuff here, so let me just do a Command-F. Maybe there's
something to do with a password.
So we see secureTextEntry.
If true, the text input obscures the text
entered so that sensitive text, like password, stay secure.
The default value is false.
Does not work with multiline=[true].
We're not using multiline=[true], and so we might be able to use this
secureTextEntry to hide the password.
Let's see if there's anything else, because there are three hits--
keyboardType.
It turns out Android can use this thing called visible-password,
but that's not exactly what we want.
And so we can actually use this secureTextEntry prop.
So let's actually pass it in, so secureEntry=true.
And it turns out there is shorthand for this as well.
If you ever have a Boolean prop, and you're passing and true,
you can just leave that all out.
And this defaults to secureEntry=true.
Let's try that.
And all of a sudden, we have username, and we have password.
Oop, not working.
secureEntry-- secureTextEntry is what we're looking for.
So now, if we start typing in a password,
oh, it does what we're looking for.
And same with username.
All of a sudden, we have authentication.
Great.
So now our Contact app is pretty much done.
We have an API that goes ahead and fetches all of these contacts.
We have authentication that allows us to only log in if we're the user
that we say we are.
And if we are not, then it will tell us exactly that we're not.
And that's all the--
all of the features that we're looking for.
Unfortunately, our-- we have some stuff hardcoded in our login screen
that doesn't need to be.
Do you remember how, in our application, when we had a fetch to some URL,
the login screen doesn't really care what URL we're looking for?
It only really cares that we're trying to log in.
So maybe we should do what we did in the application class
and abstract this out.
So again all of this logics doesn't need to be in this particular screen.
So maybe we should abstract it out into that api.js
file that we'd created earlier.
So let's go ahead and do that.
So just like we're exporting a const called fetchUser, let's export
a const called login.
And it's going to be an async function that takes two arguments.
It's gonna take username and a password.
And what's it going to do?
Well, it's going to do all of this.
So I just cut and paste everything from the other file.
And we have-- basically where we were when we
were writing that fetchUsers function.
We have a bunch of logic, and some of the logic
was a little bit specific to the class, namely
this.setState and this.props.navigation.navigation
Those two things don't really make sense in the context of this particular file.
And so let's go ahead and handle those slightly differently.
But this part is mostly correct.
So we have username and password.
What we're going to do is we're going to send a fetch request to localhost:8000,
whose method is post.
Their headers are set correctly.
Now, rather than using this.state.username,
we only need username and password.
And if you remember their shortcut or shorthand--
if we have keys and values that match in an object, we can use the shorthand.
And so now, we have the first five lines.
We have the response should be awaiting this fetch request.
That sends a post to you that URL.
Headers are telling the server that JSON is incoming.
And the body is just the stringified JSON form of this object
that we create with a key of username, with the value username there,
and a key called password, whose value is the password that we pass in here.
So what are we going to do with this function?
Well, if it's not OK rather than--
or if it's OK, rather than doing this, let's actually just return true.
It worked.
Yay.
If not, let's actually throw an error.
So rather than setting the state here, let's actually throw or create
an error.
So if it wasn't OK, we know that we're going to extract the text
and get that error message.
And let's actually throw an error here.
So say, uh-oh, this didn't work as expected.
I'm going to create an error.
So I don't know if we've talked about the syntax before, but by saying throw,
that means we're going to create an error here.
And what error are we going to create?
Well, it's a new error.
And we're going to pass it in as the message, the error message
that we extracted from this text.
And so now, login, if it worked, it's just going to return true.
Otherwise, it's going to throw an error.
And so let's actually handle that.
So let's go back into the login screen.
We're going to need to import this login function from the API file
that we created.
And now, what are we going to do?
Well in this login function here, we're going
to try to log in using the password and username from state.
And what are you going to do with it?
Well first, let's do a wait.
So remember, if it works, it's going to return true.
And what happens if it doesn't work?
It's going to actually throw an error.
If you think back to lecture, how do we handle errors in async functions?
We do this try and catch.
So now, we're attempting to do this.
So try to do this line.
So try to log in with this.state.username,
this.state.password.
If it works, whoohoo.
But what happens if it doesn't work?
It's going to throw an error.
But if it's throwing an error, we don't want
to like crash our entire app with that red screen.
We actually want to just catch that error and do something with it.
Because if you remember, that error message
we want to eventually show back to the user.
And so we're going to do that here.
We're going to catch that error, and we're going to handle it.
What are we going to do with it?
Well, we're going to take that message.
So I happen to know, because I read the documentation on error,
that the error message is stored as a key called .message,
so I can grab that.
And then, what do we want to do?
Well, we want to set the state so that the error that we show to the user
is that error message.
So we now have handled the case where it doesn't work.
What do we do if it works?
Well, we want it to do this.props.navigation.navigate
to the other screen, whose name I deleted.
But if I remember correctly, it was main.
So let's go with that and hope.
So if this worked and didn't throw an error,
we can go ahead and navigate to main or log in.
If it didn't work, we're going to catch whatever error this throws,
grab the message out of the error, and set the state.
So let's go ahead and save and run this.
Press to log in.
Missing username or password.
It went ahead and worked.
So if you remember, username and password, right now, are empty strings.
And so when we send this over, it says, oh, username and password
are both false, so it's a bad request.
It's going to return that 400, which is going to trigger an error in that login
that we wrote in API.
That error is going to get caught here, when we say catch error.
It's going to extract the error message out and then set the state.
And then, where does that eventually show?
Well, it shows in this text block here.
And so it's a slightly different logical path as we had before
but it does the same thing.
And now, it's super nice, because in this login screen,
does anything here rely on our API being at that localhost address?
Not really, right?
The login screen doesn't care where we're sending our login requests to.
It only cares that we're sending a request to log in.
And so we've gone ahead and abstracted all
of that out and exposed, via an API from our file called API that has a login
function, that login function.
And so it can go ahead and invoke this login function
with this username and password.
And that login function will return if it's true,
and it will throw an error if it didn't work.
And so we've gone ahead and abstracted out all of that--
the internals of how we're communicating with our external API and just
expose this login function to this login screen.
And so the login screen doesn't really care about how we talk to the API.
It really only cares about this function called login.
And so this is a much cleaner way of keeping only what the component cares
about within the component.
Any questions on that?
Great.
For people online if they have any questions,
feel free to send them in Slack.
Otherwise, I will see you next week for a guest lecture
with the founder of Expo.
And so we've been using Expo all class for demos and for running our projects.
And we're lucky enough that next week, we
have the person who founded Expo coming and giving a guest lecture
on all of the goodies in the Expo SDK.
So we can play around with all of those Expo components next week with Charlie.
Thank you.