[PIANO PLAYING]
SPEAKER: OK, let's get started.
Welcome back, everyone, to CS50 Beyond.
Our goal for today--
this is the second-to-last
day of the course.
And goal for today is going
to be to pick up really
where we left off yesterday.
So yesterday we introduced a new
JavaScript library called React.
And React was designed to
make it easier for us to build
interactive and dynamic web pages.
And we did so by creating components and
ultimately via declarative programming,
where we were describing what it
is that the page should look like,
whether certain variables should
belong in certain places on the page,
whether we should be iterating
over a particular variable in order
to display a list of things on the page.
And then the only thing we needed
to do in order to edit the page was
rather than go into the dom
and manipulate the dom and say,
update this element or update
that element, we would just say,
change the data.
Set the state of the component
to be something different.
And the page would automatically
render and re-update
the parts of the page that needed to
in order to reflect those changes.
And so the goal of today is to continue
along those lines and in particular,
emphasize thinking in
React, thinking about how
it is that we take an
application that we're going for,
decompose it into pieces, and then
try and put it back together, such
that we can figure out what
components, what state we
need to keep track of in
order to make our applications
interactive and useful.
Along the way we'll talk about
things like React lifecycle
and about how to get your
React applications, which
are front-end applications, to interact
with the back end, like a server,
for instance, connecting to APIs then.
And then we'll have some opportunity
for hands-on projects, as before.
And so we'll start by taking a
look at an example from yesterday
and begin to answer
some of the questions
that yesterday I pushed off until today.
And so we'll take a look at
counter.html And counter.html,
you'll recall from yesterday,
was just an application
whereby it displayed a number
that defaulted to the number 0.
And then we had increment and decrement
buttons, or plus and minus buttons,
where all those buttons' data will
change the value of the number.
We started with 0.
We can go 1, 2, 3, 4, 5 by clicking the
Increment button plus the Minus button
in order to go back down.
And ultimately the way
that we implemented
that program was to say
inside of counter.html,
we had some state inside
of our counter component
that initially was just
set to the number 0.
And then when we click the On-click
button for the plus or minus buttons,
we would call the increment
or decrement functions.
And all those increment and
decrement functions would do
is call the this.setState method,
which is a special method in React that
takes the state and updates it,
in particular by taking the count
and setting it to whatever the count
minus 1 is in the case of decrement,
and in the case of increment,
taking the count and adding 1 to it.
Of course, this state only
exists when this component
is loaded onto the screen.
And as soon as we close
the page and reopen it,
we're getting a
brand-fresh, new component.
It's remount onto the screen.
And as a result, if I increase the
number to, say, 5, for example,
close the page, and then reopen the
counter.html, we're back to 0, right?
We reloaded the page.
The JavaScript is rerun.
And as a result, our state is
reset back to the initial state
as defined by the constructor,
which, in this case, is the number 0.
Now, before we got to the
world of React and we were just
dealing with plain JavaScript,
how do we solve this problem,
if we wanted the state to persist
between when you open the page,
closed the page, open it again?
We cached it in a place
called local storage,
a place within the
browser, whereby we could
say I want to store the state of the
application inside of local storage,
such that later on in my application
I can refer back to local storage
and say, get this value out of local
storage and put it into my application.
And in React, we can do
something very similar.
Every React component has a number
of what are called lifecycle methods,
or functions that are going to run at
a particular point in the component's
life.
And one of the most common
is a special function
in React called componentDidMount.
There are a number of others.
The only one we're going to talk
about today is componentDidMount
And componentDidMount is
a special method in React
that is going to run as soon as a
component is mounted into the dom.
In other words, as soon as we
add this component into the dom,
this is the method that's going to run
before it actually ultimately renders
the contents of what's going to show up.
So any work we want to do of,
like, setting up the component,
we can do inside of componentDidMount.
And so one thing we can do, for example,
is get something out of local storage.
I can say, local
storage.getItem count in order
to say, all right, let me get the
count value out of local storage.
And I'll save that inside of a variable
that I'm just going to call count.
So if there was an item
called count in local storage,
I'm going to extract
it from local storage
and save it inside of a
variable called count.
Now, there's a chance that there
was no count item in local storage,
that this is not equal
to any particular value.
And so it's possible that count
is going to be equal to null.
So let me just do a check.
Let me check if count
is not equal to null,
meaning there actually was some
count saved inside of local storage,
well, then what I'm
going to do is I'm going
to call this.setState, setting the count
equal to the result of parse int count.
It's going to be stored as a string
so we'll parse it as an integer
in order to update the
state of the application.
So componentDidMount, if you think
about the chronology of things,
is going to happen after we've
constructed this component
and inserted it into the dom.
And we want some initial
code to be running
at the beginning of
this component's life,
whereby we're saying from local storage,
let's extract the counter variable.
And as long as it's not null, then
go ahead and update the state,
setting count equal to
the result of parsing
the int of whatever the value
of the count variable is.
So now we have a component that
can read from local storage
and update its initial state
based on that information.
What's the missing half of this now?
Yeah?
AUDIENCE: [INAUDIBLE]
isn't count equal to count?
Why [INAUDIBLE]?
SPEAKER: So the way
local storage is going
to store things is it's going
to store the values of strings.
And so that's the way that most browsers
choose to implement local storage.
And so when we read
them from local storage,
we have to assume that what's
coming into us will be strings.
And so we can parse them into integers
in order to be able to use them.
So we've got saving things-- or
retrieving things from local storage.
And now that I just gave away
the missing piece of this
is saving things into local storage,
whereby we're getting the item count,
but we're never setting the item count.
And so we want this to happen
before the window goes away.
If ever I try to close the window,
we want something to happen there.
And so we'd like to add
some sort of event listener
for when I try to close
the window, for example.
And oftentimes you might
see other web applications
that have implemented
something similar, whereby
if you try to exit out of a
web document before saving,
you might get a warning
message that says,
are you sure you want to leave this
page without saving your changes,
for example.
Or you haven't completed this action.
Are you sure you want to do this thing?
And so just in the same way that we
were able to add event listeners to when
the dom is done loading or
when you click on a button,
we can also add an event
listener to the window
to say, like, before this
window's contents are unloaded,
let's run some particular code.
And so to do that, I'll say
window.addEventListener.
And the name of this event
that we're going to be using
is a special event called before unload.
In other words, before this
window's contents are unloaded,
let's run some code.
Before we get rid of this
component, let's run a function.
And that function is going
to say local storage.setItem.
And then setItem, again,
takes two arguments.
The first is the key, the name
of the thing that we're setting.
And the second is the value,
what value it should take on.
The key, as with line 22, we
got the item with key count.
So we should be setting
the item with key count.
And what is the value?
What should we be
setting count equal to?
Yeah?
AUDIENCE: This.state.count?
SPEAKER: This.state.count, perfect.
We want to set count equal to
whatever the current value of count
is inside of this component state.
To get to this component
state, we can say
this.state to get at the
state of the component
and then dot count it to say, all
right, let's go into the state,
extract the thing with the key
count, and use that as the value
that we save into local storage.
And so assuming I didn't
make any mistakes here,
if I go back to the
page, refresh the page,
I can increment the
counter 1, 2, 3, 4, 5.
And now if I close the
counter, that's going
to trigger the
window.beforeUnload event, which
is going to save the number
5 into local storage.
And if I open counter.html again,
press Return, all right, great,
the component now shows me the number 5.
It's been able to save that
state inside of local storage,
such that even when I closed the
page and open it back up again,
I've been able to maintain
the state of the application.
Yeah?
AUDIENCE: [INAUDIBLE]
SPEAKER: For refreshing
the page as well?
Yeah, if you refresh the page, if I go
to 10, for example, and click Refresh,
it stays at 10 because before
the contents of the page
are unloaded before the refresh, it's
going to trigger that event listener.
Yeah?
AUDIENCE: Are the items that
are kept in local storage--
like say you want [INAUDIBLE]
the program [INAUDIBLE]..
Are the items in local
storage, are they secure from--
if someone hacked into your code,
broke in, could they get into it?
SPEAKER: The items
inside of local storage
are actually quite accessible to anyone.
So anyone who could get access to
the browser could get access to it,
insofar as all I need to do is go into
inspect, and I can go into, I think,
it's application, at least in
Chrome, and go to local storage.
And over here I see a count and
also a counter from a previous time
that I was doing an
application with local storage.
So these values are highly accessible.
And you can edit them as well.
And so probably best not to store
anything super secure inside
of local storage for that reason.
You'll probably want to store
things on the server side.
And we'll talk about interaction
with server side in just a moment.
Yeah?
AUDIENCE: Will you let me
know the website [INAUDIBLE]
variables that you put in
local storage [INAUDIBLE]??
SPEAKER: Local storage
is domain specific.
So if you have domain1.com, and you're
storing things inside of local storage,
domain2.com is going to have access
to a different local storage.
And so those are kept separate.
AUDIENCE: Is there a way to make it so
local pages use the same [INAUDIBLE]??
SPEAKER: So, yeah, if they're
within the same domain,
they can use the same local storage.
For example, if I were
to create another file--
and I'll just demonstrate.
Like, if I just copy counter.html
and call it, like, newcounter.html,
which is a different file,
and I open up newcounter.html,
it's still going to have access
to that same local storage.
It still has access to the number 10.
So it can draw up on those same values.
Other things?
Yeah?
AUDIENCE: Can you just explain a
little bit more of the difference
between componentDidMount and why
do we need didMount [INAUDIBLE]??
SPEAKER: Yeah.
So componentDidMount,
there are a number of what
are called lifecycle methods in React,
which are canonical places where you
can put code, and you
can guarantee that React
will call upon those
functions at particular times.
And so in fact, so
componentDidMount is one of them.
ComponentWillUnmount
is another one of them
that React offers, which is
sort of the inverse of that,
which is when we're about to
remove information from the dom.
There's even a
componentWillUpdate for whenever
a component is going to refresh
itself and change something
about its behavior.
And so there are a lot of these
different lifecycle methods
and more than we'll
probably have time to really
be able to talk about
over the course of today.
But I'd encourage you to look at them.
There are great flow charts
online of what it looks like
and when different methods are called.
And each one is called at
a slightly different time.
And so getting an understanding
for when each of them are called
can help you to really
fine tune a component
to make sure that it
behaves in particular ways
and only running certain
code when it needs to run.
So that's local storage.
So a couple of people were asking about
how do you save React component states,
such that it's available the
next time you load the program.
And that's certainly one way to do it.
And so far with regards to
React, all of our programs
have been just front-end
applications, right?
They just exist in the browser.
There's no interaction
with a back-end server,
with a database, like we were doing
when we were working with Flask and SQL,
for example.
And so there are certainly
ways that we can do that.
And the example that
we'll use is we'll take
an example that we did
back when we were working
and just plain JavaScript, which
is that of currency conversion.
So you probably recall that we
had api.exchangeratesapi.io.
And if I go to a particular
URL, like slash latest,
and then specify the
base being US dollars,
then I get all sorts of these different
exchange rates in terms of US dollars,
that one US dollar is equal to 1.3312
Canadian dollars, for example, and so
on and so forth.
And so what I'd like to do
is create a React application
that is going to be able to use
these currency exchange rates.
And it's going to be a fair bit more
interactive than the previous one
that we created was, in
particular, because React
is going to make it easier for us
to dynamically update the page.
So if I go in to exchange0.html,
an application we might create
is going to look a little
something like this,
whereby I have two drop-downs, each
of which I can select a currency.
This one's saying US dollars.
This one's saying
euros, at least for now.
And I can say, all right, one US
dollar is equal to that many euros.
I can say, all right, 15 US dollars
is equal to that many euros.
And it's going to dynamically calculate
for me what the exchange rates are.
If I change euros to
pounds, for example,
it tells me the exchange rates
between US dollars and pounds.
And so we can get these exchange
rates, much as any currency converter
app that you may have used before might
work, by just selecting the currencies
we want, typing in into the input
field what it is is the amount
that we want to convert, and
then see in this other field what
that converted amount is equal to.
And so this is the application
that we're going to be building.
I'll go ahead and create a new file.
We'll call it exchange.html.
And for now I'm going to copy the
contents of counter, paste it in here,
and we'll get rid of the counter
component because I don't need it.
I'll change the title to Exchange.
And what do we need
inside of our application?
Any thoughts as to what
type of information
needs to be in the state
of this application?
What things can change in the
application that we had before?
AUDIENCE: [INAUDIBLE]
SPEAKER: Yeah, the base
currency that I select.
So we need some sort of base currency
that we're storing in the state.
By default, my original
application just used US dollars
as the initial value
of the base currency.
And we also needed some way of
storing the other currency, whatever
else that I typed in.
So go ahead and type in other.
And then I think by default my
application said let's make euros
the other currency by default.
But you can switch them up.
What else could change?
Yeah?
AUDIENCE: The amount of base currency?
SPEAKER: The amount of the base
currency, yeah, so some sort of volume
that initially was 0.
And then there was also the converted
value that was also 0 to begin with.
And so I'll go ahead and create another
key inside the state called converted
and set that equal to 0 as well.
So we have this.state.
And in addition to having these
currencies, the base currency
and the other currency,
the dropdown list
also had a list of all
the possible currency.
That's a list that I might want to
have programmatically inside my code
so that it's easy to update
if I ever want to add more.
So for sake of example, I'm just
going to add this.currencies,
a new variable, just
called currencies, stored
inside of this component
that's just going
to be equal to a list
of possible currencies
that I want to allow
people to exchange between,
so maybe Australian dollars
and Canadian dollars,
each one just using their
three letter ISO code
for what the abbreviation for
that currency happens to be.
We'll do francs.
We'll allow those.
Chinese yuan we'll allow.
Indian rupees we'll allow.
We should do US dollars and euros.
We'll do pounds, yen.
And we'll also do New Zealand dollars.
So there are more
currencies than just this.
But we'll just do a small sampling of
currencies for the sake of example.
Semicolon there, and, OK, we've defined
the initial state of this component.
And now let's render the component.
What should it actually look
like when we try and render it?
Well, if you remember
what the page looked like,
there was a select
menu and an input field
up top for the base currency
and then a select menu
and an input field underneath
it for the other currency.
So I'll go ahead and create two
divs inside of my outer div.
The first of which is going
to be for the base currency.
And the second of which is going
to be for the other currency.
So the base currency is
going to have a select menu.
And the select menu is going
to have a name of base.
And for now I'll just say select name
as base and slash select to end it
that we'll add to this in just a moment.
And in the other div, I'll have a
select menu, whose name is Other.
And that'll also be a select menu.
So I now have a base select menu
on top of another select menu.
And the value of this
select menu, like which
item is chosen in the select menu, is
going to be represented by which part
of the state, base, other
value, or converted?
OK, someone said it, base.
So this.state.base, that's
going to be with a value
that this select menu takes on because
I want to bind to the select menu
effectively to part of the state.
And the part of the state that I
want to connect it to is the base.
Whatever base currency,
that's the option that
is selected in this drop-down menu.
And likewise for the other select
menu, as you might imagine,
the value of that select menu
is going to be this.state.other.
Now, inside of each select
menu I want options.
I want a option, a tag, for
each of the possible currencies.
And so to do that, I'm
just going to go ahead
and map over all of the currencies.
So I can say this.currencies.map
and each currency.
So I have an array of currencies.
I'm mapping over each one, one at a
time, taking each individual currency
and turning it into an option.
That option's going to
show up with a text.
Text of the option should just
be the name of the currency.
And the option needs to have a key
because React requires anything
that you iterate over to have some
sort of key to uniquely identify it.
The key you can just be
the name of the currency.
And the option also
needs to have some value.
And the value, in this
case, is also just going
to be the name of the currency.
Because when I select
an option, its value
is going to be the new
currency that I want to use.
I can go ahead and take
this code and use it
again inside of the other select menu.
You can imagine factoring
this out into a variable
and then inserting it
to avoid redundancy.
And so now let me go ahead
and open up exchange.html.
Now what we see is I
have two drop-downs, one
that defaults to US dollars but that
shows me all the possible currencies
I can choose from, and
one that defaults to euros
that also shows me all of the currencies
that I can choose from as well.
So all right, we're making progress.
But of course, if I try
and choose something else,
if I try and choose Canadian dollars,
for example, from this select menu,
it stays as US dollars.
It doesn't change because
my state is never changing.
My state is always base is US
dollars and other is euros.
So I need to have some code that
says when I change the select menu,
let's go ahead and actually make that
selection so to the select menu I'll
add in onChange handler.
So when the select option changes,
we'll go ahead and call a function.
And I'll call it this.makeSelection.
But you could certainly
call it whatever you want.
Likewise, when I click
on another currency,
I'll also say, when that changes,
let's call this.makeSelection.
And now it's going to be
a function I want to run.
I'll go ahead and add a
makeSelection function.
It takes the event.
And we're going to go ahead and
update the state, this.setState.
And what about the
state needs to change?
Well, I need to change either
base or other, depending
upon which select menu I selected.
And the way I get at which one I
selected is via this name attribute.
The base select menu has
a name attribute of base.
And the other select menu has
a name attribute of other.
So if I want to set the state, I want to
set the key of whatever event.target--
remember, event.target is
the select menu itself--
whatever event.target.name is.
If it's base, I went to update the base.
If it's other, I want to
update the other thing.
And what do I want its value to be?
I'll go ahead and say
event.target.value.
So, OK, let's give this a shot.
I refresh the page.
I have US dollars.
If I change it to something else,
all right, it actually changes.
If I change this one, that
value changes as well.
So I'm now able to change
these select drop-downs.
So I've got the drop-downs.
Let me go ahead and now
add the text fields.
So I have these select menus.
And I also want an input field,
whose value is this.state.value.
Recall that inside of our
state we're storing a value
called value, which is going
to be the base currency value,
and a value called
converted, which is going
to be the converted currency value.
So inside this input field we're
going to store this.state.value.
And inside of the converted field,
we'll go ahead and also store input,
whose value is this.state.converted.
And I'm going to add
an additional attribute
to this input field called disabled.
A disabled input field just
doesn't allow you to edit it.
I only want people to edit the top
input field, not the bottom one.
So if I said disabled
equal to true, that's
not going to allow anyone to edit the
converted field, only the base currency
field.
So I refreshed that.
And great, now we have something that
looks a little bit closer to what
we might expect, whereby
I have drop-downs,
where I can choose the currency.
I can choose a currency.
And I have these two
input fields, one of which
I could actually type things into, and
one of which is none, not changing.
Of course, this input field,
much like the input fields
we've seen before, if I actually
try and type something into it,
nothing's actually happening.
It's just staying at 0.
And the reason for that is I need
to add some sort of change handler
to say when I type something
into the input field,
we need to actually update
the state of the application.
We need to change what's
inside this field.
So on this input field I'll
add onChange attribute.
And that's going to be
equal to this.changeValue.
Again, changeValue's just an
arbitrary name I'm choosing.
But it's going to be the name
of the function that's going
to change the value of the input field.
So here's that changeValue function.
And changeValue's also
going to update the state.
And it's going to set the
value to event.target.value
for updating the value
inside of the state anytime
I try and change the input field.
So now I have an interface that works.
I can choose different currencies
from the drop-down menu.
I can type numbers, or letters
theoretically, into this input field.
But the currency conversion
isn't actually happening.
So this is going to be the last
step of actually doing the currency
conversion when I type
something into the input field.
And so how are we going to do that?
Well, I want some function
that's going to recalculate
the converted value based on all
the information inside of my state.
So before I do anything
about the interface,
let me just add that function.
I'm going to add a
recalculate function, which
is going to be a function that is going
to take care of the process for me
of making the API requests
to exchangeratesapi.io
and figuring out what
the exchange rate is,
multiplying it by the amount of
value that I want to convert,
and then displaying
that value on the page.
So let me go ahead and
first say this.state.value
is the input field where
I'm storing information
about what it is that I've typed
into the base currency input.
I'm going to go ahead and
parse that into a number
because right now it's a string.
ParseInt is a function we've seen
done in order to parse an integer.
But of course, the thing that
I type into the input field
might not be an integer.
It could have a decimal point.
I could be trying to convert,
like, $2.80, for example.
And so I want to say parseFloat
instead of parseInt to say treat
this number as a floating point number.
So it's not a function
we've seen before.
It behaves basically the
same way that parseInt does.
And I'll say that inside of a
variable that I'm going to call value.
Questions so far about what
I'm doing or why I'm doing it?
All right, there's a chance that this
value is not going to be a number.
And so there's a special value
or special function in JavaScript
called isNaN, in other words, is
not a number, that takes an argument
and just tells you whether it's a
number, whether it's not a number
or not.
So for example, if I
pass value into isNaN,
it'll return true if value
is not actually a number.
And so that's useful for me
because if it is the case
the value is not a
number, then I don't want
to actually do any more calculation.
I just want to return.
We're done here.
So, so far, I've taken whatever I typed
into the input field, tried to parse it
as a floating point number, saved
that value inside of this variable
called value.
And if it's not a number, then just
go ahead and exit the function.
There's nothing else
that we want to do here.
But if we keep going, if it is
a number, then let me go ahead
and run a fetch query,
whereby I say OK, let's
fetch from
https://api.exchangeratesapi.io/latest.
Base is going to be-- and then
I'll plug in a value here.
And the value I want to
plug in is this.state.base.
Again, this code, basically
the same as the JavaScript
code that we were using before in order
to do the same thing without React.
But I'm making an API request via
fetch, requesting data from a server.
I can type in URL here to try and
request data from that server.
And right now I'm getting
stuff from Exchange Rates API.
But you could imagine getting
it from something different.
You could run a Flask web application,
for instance, hosted on Heroku
and try and fetch something
from that Flask web application.
And so here we can use the
React front end to combine
with just about any back end.
Here I'm using the Exchange
Rates API back end.
But it could be any Flask web
application that you create
or web application server that
you create in any other language.
It doesn't really matter.
React will just be able to request
information from that back end.
So I fetch it.
And then the syntax here
was a little bit strange.
But you might recall that
we then said, all right,
then let's take that response
and convert it to JSON.
And then with the resulting data,
do something with that data.
Was there a question here?
Yeah?
AUDIENCE: [INAUDIBLE]
SPEAKER: Yes, so these here are
back ticks around the string.
And the reason I'm using back
ticks is that's how JavaScript
does format strings, whereby if I want
to insert a value into the string,
like this.state.base, you surround
the string with back ticks.
And then you use dollar sign and
curly braces to insert a value here.
It's effectively the same as putting the
lowercase f in front of a Python string
to insert values there.
Every language just deals with format
strings a little bit differently.
We get back the data.
And if we remember what the
API response looks like,
it comes back as a big JSON object
that looks something like this.
And it has a key called rates.
And inside of that key called
rates is this big object, where
a key is going to be some
currency, like New Zealand dollars.
And the value is the exchange rate,
1.47 New Zealand dollars for every 1 US
dollar.
And so once I have this data inside
of data.rates and then indexing
into rates this.state.other, that's
going to get for me the exchange rate,
because the inside of data,
we're going into rates.
And inside of rates, I want
whatever the other currency is.
That's the exchange rate that I want.
So I want to go ahead
and say this.setState.
Remember that converted was the
name of the part of the state that
referred to whatever the value of
the converted dollar amount was.
And I'm going to set converted
equal to data, get at the rate,
get at the exchange rate.
Is this going to work?
Is this done?
There's a mathematical bug here.
Yeah?
AUDIENCE: You need to
multiply it by [INAUDIBLE]..
SPEAKER: Yeah, I need to
multiply it by a value.
Because if I want to
convert $2, then I want
to take whatever the
exchange rate is for $1,
and multiply it by 2 to get whatever
the converted amount actually is.
So the converted value's just
going to be whatever the exchange
rate is multiplied by the value.
So I'm never actually calling this
recalculate function anywhere,
although this recalculate
function does now hopefully
actually work in terms of
getting the exchange rate,
multiplying it by whatever the input
value is, and then updating it.
So what I'd like to do is say, any time
I change the value in the input field,
let's go ahead and recalculate.
And so you might
imagine doing something,
like, OK, this.setState
and then this.recalculate,
might be an easy thing to imagine
as reasonable logic to do.
Let's update the state, setting the
value to event.value.target, and then
do the recalculation.
There's a small problem, though, in
terms of the way the JavaScript works.
A lot of JavaScript is
asynchronous, meaning
that we might be running this
code, this.setState, and then
immediately run this.recalculate,
even if the state isn't yet
done being updated.
Like if it happens to be taking
the browser a bit of time
to update the state,
this.recalculate, in theory,
could be run before this.state is done.
Like, this.state is going
to be executed first,
but it could take some time
separately and asynchronously.
And we might end up running
this.recalculate too early.
And so this is a common
enough paradigm in React
that there's some special
syntax for doing this.
This.setState can actually
take two arguments.
The first argument is the change
that you want to make to the state.
But you can also pass to
this.setState a function
that you want to run after the state
is done updating, not something
that we saw yesterday.
But if I say, all right, let's
set state to this new value,
and then give this.setState
another argument and say,
all right, this.recalculate,
what am effectively saying here
is, yeah, go ahead and
set the state, setting
value equal to event.target.value.
But when you're done
setting the state, then
run this this.recalculate function.
And that will happen only after
the state is done executing.
So it's this optional
additional argument
I can give to this.setState to run some
calculation after the state has fully
updated.
And so that's what I'd like to do.
When I set the state,
I'd like to recalculate
after I've done that update.
So let me go ahead now
and refresh the page.
And we'll give this a try.
0 US dollars equal to 0 euros.
I'll go ahead and delete
and type in the number 1.
And we see the number of euros
corresponding to 1 US dollar.
I type in the number
2, and I see the number
of euros corresponding to 2 US dollars.
Amazing.
Where's a bug in this application?
Not quite perfect yet.
Yeah?
AUDIENCE: But if you change [INAUDIBLE].
SPEAKER: Great.
If I change the currency, if I say, OK,
I don't want to convert doing euros,
I want to convert between Chinese
yuan, for example, and I change it,
nothing about the input field changed.
And why is that?
Well, when I made a selection
via this makeSelection function,
I'm updating the state, updating
the value of the drop-down.
But I'm not doing the recalculation.
So to fix this, I probably want to
also say, after you make a selection,
let's go ahead and also compute
this.recalculate in order
to recalculate the correct value.
So I'll go ahead and run this again.
0 US dollars, 0 euros.
If I say 2 US dollars, I
get that number of euros.
If I change it to Japanese
yen, then I see, OK, now we
see an update for the number of Japanese
yen that corresponded to US dollars.
And it works the other way around too.
I can go here and change
this to Canadian dollars,
and it changes to show me
how many Japanese yen are
equal to 2 Canadian dollars.
Questions about any of
that and how it worked?
All right, a couple changes that
we could theoretically make here.
One thing is if I have 2
Canadian dollars and I delete it,
like just turn it into
the empty string, we
end up with a situation where the
number beneath stays the same.
So you could add some
logic here in order
to try and handle this particular
situation, whereby I could say,
when I change the value of the input
field, if ever the converted field--
I can update the converted
field as well, whereby--
well, actually, I'll
backtrack a little bit more.
Right now what's happening
is if I make a change,
like change from 2 Canadian
dollars to 22 Canadian dollars,
there's going to be
some sort of lag here,
whereby I'm making a
request to the server
in order to make requests
from the Exchange Rates API.
And when I make that request
to the Exchange Rates API,
then it's going to update this field.
And so what I might
like to do is instead
of just leaving the converted
field as is, change it
to something like calculating
or some sort of message
to indicate that it is calculating the
exchange rate before it actually gives
me back what the exchange
rate actually is.
And so to do this, I could
do something like this.
When I change the value
of the input field,
let me also set the value of converted
equal to null, for example, meaning
let me just clear out the value of the
converted field, set it equal to null.
And then in this input
field, rather than give it
just a value of
this.state.converted, let me say
is this.state.converted equal to null?
If it is, then let me
just say, calculating.
But otherwise-- and again,
this is the ternary operator
that we took a look at yesterday.
Otherwise, then it can
be this.state.converted.
So a little bit of extra logic here.
But the logic on line 39 is saying
what should show up in that lower input
field if my converted value has
null, meaning I haven't given it
an actual number yet?
Then go ahead and display
the word calculating.
But if there is an
actual number there, then
go ahead and just display that number.
And so now the result is going to be--
and the API seems to be
working rather quickly today.
So maybe we won't be
able to notice this.
But if I type 2, and I change
it to 21, yeah, there's
a brief moment where
you can see that it's
calculating before it actually updates
with what the correct answer is.
So there's never a state where
I see conflicting information
that the other field
just hasn't yet updated.
It will always just say calculating
until I've calculated the number.
Yeah?
AUDIENCE: [INAUDIBLE]
SPEAKER: This.state.converted is part
of the state that represents whatever
the value of the converted currency is.
And I have that just because I
defined it initially in the state.
I called it converted.
And I'm updating it whenever I want
a new value for that input field.
The only small corner case here is
that now if I delete and I press Delete
again, go to an empty input
field, the other field is just
going to say calculating
forever because it's never
going to end up getting a number.
So you could go through
and you could fix this
by adding a couple more conditions.
You can see the code in
the source code examples
that I posted online that'll help
walk you through that as well.
So we now have an application
that can do currency conversion
between different currencies.
You can type in different amounts of
currency and see the converted value.
You can choose different
things from the drop-down
to convert between different currencies
but there is some inefficiencies here
still.
Any sense for where the inefficiencies
might be or room for improvement?
So one thing you'd might notice is
that any time I change the input field,
or anytime I select a new
currency from the drop-down,
we're running the recalculate function.
And the recalculate function
is going to parse the number,
reach out to the API via web
request, get back some response,
parse that data, and save it
inside of the input field.
Which part of this might not be
necessary to do every time I type
something into the input field?
Yeah?
AUDIENCE: If you're just
changing the number,
but you're keeping the
two currencies the same?
SPEAKER: Yeah, if I'm
just changing the number,
like if I just change this from 28,
press Delete, OK, now 2 US dollars, I'm
making yet another web
request to ask the API, like,
what is the exchange rate
between US dollars and euros?
And then getting that number,
which is almost definitely going
to be the same number that
I got like a second ago
when I just did the last calculation.
And I'm reaching out
again to get that number
and then doing something
with that number, which
is costly in a number of ways.
It's costly on part of
the this web application
because I'm reaching out to the
server when I probably don't need to.
And it's costly on the part
of the API because it's
getting more requests than it
needs to handle because there's
some unnecessary requests here.
Now, sure, it's possible
that I typed in a number,
and I don't type in another number
until, like, tomorrow, by which point
the currency exchange
rates might change.
But you might imagine that if I make
two requests for the same exchange rate
within, like, a minute
of each other, odds
are that it's totally fine to
just use the old exchange rate
and not have to recalculate
or re-request from the API
what the exchange rate actually is.
And so here we're going
to implement what's
called a cache, a way that we can
just store information locally rather
than need to reach out to an API in
order to access that information.
And what we'd like to say is some sort
of model, whereby we can store exchange
rates, and if I try and
request the same exchange
rate within a minute of the last time
that I requested that exchange rate,
then don't bother making
another API request.
Just use the data that we've stored.
Question, yeah?
AUDIENCE: Is there a limit on the API?
Like, if you just [INAUDIBLE] make
the request every time [INAUDIBLE]..
Is there a certain point
where the API [INAUDIBLE]??
SPEAKER: It depends upon the API.
Certain APIs impose
formal rate limiting,
whereby you can only make a certain
number of requests per hour or per day,
for example.
This API happens to not be like that,
though they do have a disclaimer that
warns, like, please
cache your results, which
is what we're about to do, so
as to avoid bogging them down
with many, many requests for
constant currency exchange rates.
So it does depend a
little bit upon the API.
Let's try and actually
implement this cache.
And you can see this implemented
inside of exchange1.html,
if you take a look at the
source code examples for today.
So up here at the top,
we have this.currencies,
and we also have this.state.
Let me go ahead and add another
variable called this.cached,
which is going to store
a JavaScript object where
the keys are going to be currency
exchange rate bases that I've stored.
And the values are just going
to be those currency exchange
rates that I have actually stored
inside of my web browser's memory,
in this particular case.
So all the interesting logic is
not-- no longer in the interface.
The interface is not changing at all.
The only thing that's changing
is my recalculate function.
How am I doing this recalculation?
Well, let me say, when I get this
data back, before I set the state,
let's update this.cached.
Let's update what's inside
of the cache, in particular
at the key this.state.base,
meaning update inside the cache.
I need a different value in the cache
for every different base currency,
because I want to be able to store
different possible base currencies.
And I'm going to store two things.
I'm going to store the exchange rates,
which is going to be in data.rates.
And again, this is just
a JavaScript object.
And I'm also going to store a
timestamp, meaning at what time did I
insert this data into the cache?
At what time that I make
the initial request?
And save that data.
And in JavaScript,
there's a special function
called date.now, which will
just get us a date object that
represent the current timestamp.
So what's happening here?
I'm making a request to the
API, taking the resulting data,
and storing it inside of the cache,
saving it inside of some variable,
whereby that variable is going
to store the exchange rates,
and it's also going to store the current
timestamp, the timestamp for right now.
And then we're going
to go through with what
we did before of setting
the state, updating it
to whatever they exchange rate
is times the dollar amount
that we're trying to convert.
But now that we have this
cache, we can add some logic
before we make this API
request so that we don't
need to always make this API request.
In particular, we can
add an if statement.
I want to check to see
whether or not-- well, I
want to check to see two things.
One, is this base currency
already in the cache?
In which case, I don't need
to make another API request.
And if it is already in the
cache, has it been less than,
say, a minute since the last
time that I made this request?
So this is the idea of what's
called cache invalidation, the idea
that after a certain point
in time, this cache is going
to be considered stale or invalid, and
I want to go ahead and make the API
request anyway.
If I make the-- if I
check again tomorrow,
the exchange rates are
probably change, so I probably
want to get the most
up-to-date exchange rate.
But within a certain period
of time, like a minute,
it's probably not going to change much.
And so how am I going to do this?
Well, if it is in the cache, it'll be
in this.cached, this.state.base, right?
This variable is the same as this.
This is where I'm
storing the information
if it is, in fact, inside the cache.
And let me first check to make
sure it's not equal to undefined.
If I try and index into a JavaScript
object into a key that doesn't exist,
the value I get back is a
special value called undefined.
And so if it's not
equal to undefined, that
means there's actually
something in the cache.
But I also want to make sure
that it's been at most a minute
since the last time that
I updated the cache.
And so how am I going to do that.
Well, let's take the
current timestamp, date.now,
and let's subtract the
timestamp in the cache,
so the current timestamp minus
this.cached, this.state.base--
that's what's in the cache--
dot timestamp to say
get at the timestamp key
of whatever is in the cache.
And so I'm taking the current date,
subtracting the time at which point
I put the data in the cache.
And as long as that
is less than a minute,
this subtraction is going to return
to me a number in milliseconds.
So a minute is going to be 1,000
milliseconds times 60 seconds.
So long as the difference
between now and when
the time I put things in the cache is
less than 1 minute, then let's go ahead
and draw the information from the cache.
And so to do that, we'll go
ahead and say, this.setState,
setting the converted value.
Originally, the converted value was
equal to this value, data.rates,
this.state.other times value.
But it's no longer going
to be stored in data.rates.
Instead of data, the information is
stored in the cache, so this.cached,
this.state.base, get at the
exchange rates in the cache,
convert it to the other currency,
and multiply it by the value.
So fair amount of code here.
We'll zoom out, see if you
can see what's going on here.
We have a condition.
It's running a check.
The first part of the check
is saying, is there actually
something in the cache
for this currency?
Take the cache, look
up the base currency.
If it's undefined, there's nothing in
the cache, so we can't do anything.
So we're checking to make sure
there's actually something there.
And if there is something there, let's
make sure it's recent enough to use.
Let's take the current
timestamp, subtract the time
that we put the data
in the cache, and make
sure it's less than 1 minute, 1,000
milliseconds times 60 seconds.
Assuming this cache is
valid and we can use it,
we'll set the state, updating the
value of the converted input field
and say, all right, let's take the value
of the cache for this base currency,
get the exchange rate
for the other currency,
and multiply it by whatever
value was typed in.
At that point, we don't need
to go on with the function,
we've already been able
to update the state.
So I can just hit Return and
say exit the function now.
A lot of code going on there.
I encourage you to take
a closer look at it.
It's on the course website.
And if I open up exchange.html
now, the first time I
type in currency, like 1 US dollar,
it's going to calculate for a moment.
But now if I update
it, say, 12 US dollars,
the update's almost instantaneous.
You don't see calculating
and then it changes,
because there's no longer
this additional couple
milliseconds of latency of going to the
server, requesting the exchange rates,
and then using that information.
It's just going to be using the cache.
It's going to compare to whatever
current value is in the cache
to say this is within a minute.
Let me just go ahead and
update myself to go ahead
and use the value that's
stored inside of the cache.
Questions about that idea of
why we did it, of how we did it?
Yeah?
AUDIENCE: Yeah, so just in terms of
the syntax, I'm a little bit confused.
When you have this.cached bracket
this.state.base, bracket dot rates,
when do you use period, and when do you
use brackets when you're [INAUDIBLE]??
SPEAKER: Great question.
When do you use periods when you're
going into a JavaScript object?
When do you use the brackets?
Long story short, they're
basically interchangeable,
whereby if I go into the console
here, and I have a JavaScript object--
I'll call it const object--
that has a key of A and a value of
2 and a key of B and a value of 8,
for example, I can say object.a to
get at the A property of the object.
Or I could also say, object square
bracket and then in quotation marks
"B" to get at the B
property of the object.
And those will work the same way.
Generally speaking, if there is a
fixed name of a property of the object
that I want, like I know
it's A, for example,
I'll just use the dot notation,
object.a, to get at the A property.
But sometimes my program doesn't
know in advance what the property is.
Like, I have some variable called
key, which is set to B, for example.
And if I want to access the key
property, I can't say object.key.
That's undefined.
That's looking for something
inside of my object
with a key that's literally called key.
If key is a variable and I want
to look it up inside the object,
I'll need to do object
square bracket key to say,
all right, let's get at the
key property of the object.
So you could just use square
brackets for everything.
But the dot notation sometimes just
makes things a little bit cleaner.
And so I'll use that too.
But, yeah, good question and
good clarification there.
Other things?
Yeah?
AUDIENCE: Do you [INAUDIBLE] differences
between cache and local storage?
SPEAKER: Yeah, good question.
So cache, local storage, cookies,
what is the difference between all
these things?
So a cache can come in a
number of different forms.
Your computer, CPU, has
a cache that it uses
when it's reading things
from memory, for example,
that is lower level than what
we're dealing with in this class.
A cache you can think of
as just a general term
for any way of storing data in a place
that's easier to access, for instance.
So you might-- your web browser,
for example, probably has
a cache for web pages, whereby
when it's loading a web page,
it could just reload the web
page that it has in the cache
rather than try and request
the whole web page again.
And so caches come in many different
forms, and it's a very general term.
The type of cache we're
using here is just
the cache that's being stored inside
of our browser's memory that's
going to store the exchange rates.
But if I were to close
this page and reopen it,
that cache would be wiped clean
because I reset the value of the cache.
Every time I construct a brand-new
example of an app, in this case,
in this exchange rate program.
Local storage, meanwhile, you
can think of as a type of cache
whose job it is, is to store
information inside the browser,
in particular so that information
can be used by my application
later if I open it up
at a different time.
It's especially useful for
being able to store information
inside the browser that will
persist even when I close the page
and reopen it.
Because otherwise that
probably wouldn't be the case.
And that's all happening on the
client side, on the front end.
Cookies, meanwhile, you
can think of as having
to do with the interaction
between the server and the user,
the client, whereby
if you have a cookie,
you can think of it as a way of the
server keeping track of who you are,
such that if the user, the client, is
sending a cookie along with every web
request it makes to the server, if the
server sees that cookie multiple times,
it knows, OK, I know who this individual
is based upon the value of the cookie,
for example.
There are also ways for
cookies to store information
about the state of the current
user's interaction with a server,
though we haven't really
touched on that in this class.
Yeah?
AUDIENCE: Just a quick question.
Why would this.currencies in this.cached
have their own separate variables
rather than including
them in this.state?
SPEAKER: Good question.
So why is it that this.currencies,
this.cached are separate?
Currencies is a separate
because it's not really
something about the application
that is going to change,
insofar as it's basically just
a fixed list of variables.
I could have effectively
pulled this out of it entirely
and just made it a constant variable
inside my JavaScript, like said,
const currency equals
something, for instance.
And the cache is sort of
separate because nothing
about what the application looks like
is really dependent upon the cache.
It's only dependent upon the values
of base, other value, and converted.
And our recalculate function uses
the cache to give converted a value.
But the interface itself is only based
upon the value of the converted thing.
So a couple of things I'll talk
about briefly, the first of which
is going to be options that you'll
have for the morning project.
I'll talk about the
morning project first
before I dive into one last example.
And because we are reaching--
well, actually, sorry.
Before I talk about the morning
project, one thing that I should mention
is how React is typically
used in practice, whereby
so far, when we've been
writing React code,
we've been writing this React code
purely inside of this HTML page, where
we've been including in the
JavaScript section some JSX code,
JSX code being the
version of JavaScript that
allows me to have HTML elements
embedded inside the JavaScript.
This is not normal
JavaScript code and not code
that our browsers natively
are able to understand.
And this is why we've been
including this Babel package up here
in the header section of
our page, which is going
to take care of the job of
transpiling our code from JSX
into plain-old JavaScript so that
our browser can understand it.
In practice, this is not the type of
program that you would want to deploy,
at least not in this form.
And in fact, when you open up any of
the problems we've been doing so far,
you'll see this warning that says,
you are using the in-browser Babel
transformer.
Be sure to pre-compile your
scripts for production.
And what that warning
is basically saying
is rather than deploy something
that is JSX code plus the Babel
transformer, that's
basically translating code
from one language to another, and
have that translation process happen
every single time someone
opens a page on your web page,
we can just pre-compile
all of those scripts once.
Before we release our
application for production,
we can say, go ahead and
take all of that JSX code,
compile it into plain-old JavaScript
code, and let's just deploy
the JavaScript code.
That way we compile it once.
And then anyone who uses our page
can just open the page normally,
no translation necessary because
it's already in plain-old JavaScript.
And so there are a number of
tools for being able to do this.
But perhaps the most
common and most popular
is a special program created by
Facebook called Create React App.
In order to use Create
React App, you'll need
to install something called Node
or Node.js on your computer.
Node.js is just what's
called at JavaScript runtime.
You can think of it as a way
of getting JavaScript code
to run just about anywhere.
You can use JavaScript on
the server, in addition
to using it just on the client.
But if you install
Node.js on your computer,
you can then get access to a program
called NPM, the Node Package Manager.
And there's a particular
package called Create
React App, which is going to
build for you a React app that
has a lot of scripts, useful scripts and
tools that are already built into it.
And so you're not going to need
this for the purposes of this class.
But I wanted to show it to you.
Because if you ever go
into production in order
to build applications with
React, you're probably
not going to be just putting it in
the script section of your HTML page.
You're probably going
to be doing something
a little more along these lines.
So once you install
Create React App, you
can create a new React application
by just typing create-react-app,
followed by the name of the application.
So I want to create an
application called Hello.
And again, you're going to need to
install Node and then install Create
React App in order for this to work.
But I type create-react-app Hello.
It's just the name of an application.
And then the Node
package manager is going
to go through a long step of fetching
all these packages and useful scripts
that it's going to use for me.
And it's going to build for me the basis
of an application written in React.
And it's going to give me a starter
application, basically, a foundation
on which I can start to build.
Once it's done, I'm going to
CD into the Hello directory.
And if I type that last
and look what's in here,
there's all sorts of stuff
that's actually in here.
I have public, which is going
to store a bunch of files
that our web application
might use, a source folder,
where all the JavaScript code
is actually going to be located,
Node modules, which is a special
folder for including other packages,
much in the same way that
in Flask we would import--
in Python, we would import
other modules that we might use.
In Node, you can also have
modules that you install
to give you additional capabilities.
But once you're in here, the way
to run this React application
is just to say NPM run start to
actually start running this application.
And what that's going to do is it's
going to start up this web application,
and it's going to give me a default
React application that's just going
to display, I think, the React logo.
Yeah.
So this is the default React
application that React gives you
if use Create React App.
But the nice thing about
this is that it automatically
has separated things into files for me.
So that if I go into
Hello and go into Source,
my source folder has
an app.js file, where
they're defining an app component
that, again, has a render function that
says editApp.js and save to reload.
And so this is already
getting into the structure
of what most React
applications look like,
which is rather than having
all of the components
inside of the same HTML page, have
a different app.js file for the app
component and have something else
dot JS file for some other component
and just separate all of your
components into different files.
It separates the CSS into
different files as well
and just starts the process of
trying to separate things out.
If you use Create React App,
it also has dynamic reloading,
the way that your Flask app might do.
But you don't even need
to refresh the page.
Instead of editSource.app.js and save to
reload, I can replace this with Welcome
to CS50 Beyond and save that.
And without doing anything,
if I just go back to Chrome,
my page is already updated with
whatever it is that I changed.
So every time you save,
Create React App will
take care of the process
of auto reloading or hot
reloading the web page up in order to
reflect whatever I've changed, so very
useful for development, such that
you can just make a change, save it,
and the page will automatically
reload to reflect those changes.
So this is very helpful for development.
But when I'm ready to actually
build this program in order
to ship it, for example,
I would run something
like NPM run build inside
of my Hello directory.
And that's going to run a special
script that Facebook has put together
that's going to basically take
care of the process of taking all
this code in different
files and written in JSX
and compile them for me
into regular JavaScript
that any web server can understand.
And so if I go into the
build directory now,
I see that I have all
of these files here
that are basically just going
to be files that I can serve,
using any old web server without
the need to translate things
from one language to another.
And that will just work as well.
So not going to delve too
much into that because there's
a lot of details and nuances
here that are worth exploring,
and this changes from time to time.
But just good to be
aware of, in case you
decide to continue with
building React applications.
Questions before I go on about anything?
All right, so this
morning's project, you're
going to have a number of options.
I'm going to introduce one
of them in just a moment,
so we're not quite done for
the morning lecture just yet.
But a number of options
for things you can do.
One of the goals here is just to
continue giving you an opportunity
to continue working on React.
So I know many of you are still
working on or adding features
to your to-do list application
or your flashcards application.
So if you'd like to, feel free
to continue working on and adding
to those applications.
If you'd like an
interesting new challenge,
if you happen to be done with those,
or you're getting bored of them
and want to try something
different, you can
try implementing a
Connect Four game, sort
of an upgraded version of
tic-tac-toe, this time for four
in a row instead of three.
But I'll show you what that
looked like in just a moment.
And if you'd like to--
we're on the second-to-last day now.
We want to give you the flexibility to
explore, the freedom to try things new.
Feel free to start something
new of your own choosing.
Really the goal of today is to really
make the focus be about project time.
You've heard me talk
a lot this past week.
So we're going to try and
talk a little bit less
today and give you
more of an opportunity
to really try things hands
on, because you've probably
found by this point in time that really
the best way to learn this material
is to be working with it.
Try things out.
Try and get features to work on your
own and see what happens ultimately.
And so these are going to
be the potential options.
If you choose to implement the Connect
Four game, the types of features
you might want to consider,
displaying a board
and which player's turn it currently is.
When the user clicks
on a column, the turn
changes from red to
black or black to red,
as the typical colors
are for Connect Four.
If you click on a column, that drops
a circle inside of that column,
for example.
Don't allow clicking on a
column that's already filled,
if it's filled to the top of what's
usually a seven-by-seven grid.
And when someone has four in a row,
you can display who the winner is.
And that will probably be the last step.
If you choose to try and implement
this project, when it's done,
it's probably going to look
something-- it could look something--
a little something like this,
whereby you have a Connect Four
and who the current turn is
in a big seven-by-seven grid.
And this, again, could just
be an HTML table, for example.
I've implemented on mine
some additional features
for detecting when the
mouse is happening.
And there are things like onMouseEnter.
There's event handlers
you can do for that
so that when you hover over
columns, that you can see the column
highlighted, though you don't
need to implement the feature,
at least not initially.
You click on a column, and, OK, that
drops a red circle into that column.
And, OK, now it's black's turn.
And so black can click somewhere in
order to play a black circle there.
And we can continue this game
until someone gets four in a row.
And when someone gets four in a row,
the winner of the game is black,
and we display who the winner is then.
So an option for
something that you can try
to implement if you're looking
for an interesting challenge.
And let's think about the type
of state that you probably want
to store inside of this application.
What state would you want to store?
So even if you're not
planning on programming
this, let's at least plan it out.
Think in React.
Think in our minds about what
the state of this looks like,
what sort of event handlers
we would need for it,
what is the structure
this is going to be?
Yeah?
AUDIENCE: [INAUDIBLE]
SPEAKER: Yeah, a list of
lists probably makes sense.
We've got a seven-by-seven grid.
And you have this interesting
idea, which is probably
a good one, which is
that each list might
want to be a list of all of
the columns, like a column
of lists for the first column, the
second column, the third column.
Because that's going to make it easy
to implement the idea of dropping
a circle into one of the
columns, where if we have this
is just like an array that
is black and then red,
if I try and add
something to this column,
it's as simple as just appending
something to the end of that array,
for instance.
You just add to that array.
And that's going to result
in this being reflected.
Now, if you design it with each
column being a different array
inside of your application,
you'll need to think a little bit
about how to make rows appear
inside of your table, for instance.
But, yeah, that's good thinking.
So an array of arrays
storing in the state
to store the current state
of the board, what else
are we going to need
to store in the state?
Whose turn it is,
great, is it red's turn?
Is it a black's turn probably also
something you want to store as well.
And what sort of event handlers
do we need on this board?
How does the user
interact with this page?
We've seen a lot of event handlers.
We've seen the onChange event Handler
for when someone typed something
into an input field, for example.
What else have we seen
that might be useful here?
AUDIENCE: [INAUDIBLE]
SPEAKER: On click, yeah.
And you probably want
something like that,
that for each of these
table cells, we have
some sort of on-click mechanism for
if you click on this table cell,
well, that's going to correspond to
dropping something in this column.
And there's probably no difference
between clicking here and clicking
here.
Because either way, it's
still clicking inside
of the same column, given
the nature of the game.
And so you can think about, all
right, when someone clicks on a cell,
you probably want to ask a
question, OK, what column is it in?
Then you might want to ask a question
like, is the column already filled?
In which case, well, we can't
add anything more to it.
But if it's not already filled,
then we can say something.
All right, let's go ahead and add
something to that particular array
inside of our application state.
So these are the sorts of questions, the
sorts of things to be thinking about.
Before you even write a single
line of code, think about,
what are the components
of the application?
What is the state of the
application going to look like?
What sort of event handlers
are you going to need?
And this is the way to begin thinking
about things inside of React.
Yeah?
AUDIENCE: Is that [INAUDIBLE]?
Or is [INAUDIBLE]?
SPEAKER: Good question.
The way I have implemented
this, and the way
you can certainly consider doing
it, is this is just an HTML table.
But inside of each table cell,
I've inserted an SVG target, inside
of which is just going
to be an SVG circle.
And as you remember, a circle
just has the center x-coordinate,
center y-coordinate, radius,
and also a fill color.
And these are just fill color
red and fill color black.
And if you really want to get fancy
and implement the hover feature,
all the hover feature is, is when you're
hovering over a column, let's go ahead
and turn the cells gray.
And if there's nothing in a cell,
rather than having nothing there,
have a white circle instead of
a red circle or a black circle.
And by doing that, you're
able to get this effect
of this hover, where there's just
white circles across the entire grid.
But when you hover over a column,
the background turns gray.
And so you get the effect
that you might expect,
that looks visually
interesting, but really is
just a bunch of circles
and colors changing
in terms of the way this
is actually implemented.
If you're looking for a
simpler version of this,
feel free to try and take
the tic-tac-toe application
that we originally made in Flask and
just reemployment that using React.
And that might be a good starting
point as a place to begin
and then building up
to something like this.
But there are a lot of possibilities.
And so goal for today is really to give
you a lot of time for hands-on practice
in order to work on that.
So questions about morning project?
All right, so a couple
things on a logistical note.
So we only have today and
tomorrow left in CS50 Beyond.
Just see you all know, tomorrow's
probably going to be a short day.
We're probably going to wrap
up probably around mid-day
and not have an afternoon session
tomorrow, so wrap a bit early, give you
most of Friday to have off in order
to enjoy the weekend before classes
begin again on Monday.
And one other thing that I'd like
to ask you all to do before you--
right now before you actually
start working on the morning
project is to fill
out our feedback form.
So just try and get this some
feedback before the course is over.
If you go to cs50.ly/feedback, you'll
find an anonymous feedback form,
where you can leave anonymous feedback
about your experience in this class
over the course of this week.
Goal for this is just very
good useful data for us
in order to be able to
help improve the class.
This is the first time that
we are offering CS50 Beyond.
And so all of this is sort of
brand-new curriculum and content
and organization.
So curious to get your impressions
on what you think the strengths were,
what things you would change, or
what things you would recommend
as improvements for the future.
We'll definitely read all of
this feedback, and so all of it
would definitely be very helpful.
So please go ahead and fill
out this feedback form.
When it's done, feel free
to dive into your projects.
We'll work on these projects now in
the morning, here in this auditorium,
until about 12:30, at which
point we'll break for lunch.
We'll come back at 2:00 for
a couple of more points,
but mostly to spend the afternoon
focused on working on projects.
And so we'll break for now and
let you work on those projects.
