Placeholder Image

字幕列表 影片播放

  • [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.

[PIANO PLAYING]

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

在React中思考--2019年以後的CS50》。 (Thinking in React - CS50 Beyond 2019)

  • 2 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字