Placeholder Image

Subtitles section Play video

  • [MUSIC PLAYING]

  • BRIAN YU: OK, welcome back everyone to CS50 Beyond.

  • We continue where we left off this morning.

  • This morning we introduced React, a JavaScript-based framework

  • to allow us to quickly and easily build user

  • interfaces via declarative programming, describing

  • what it is that we want the page to look like in terms

  • of various different components.

  • Then putting those components together to be

  • able to create an application that's interactive.

  • In order to update the application, we didn't

  • need to do what we did before in the world of imperative

  • programming of saying, go to this part of the page

  • and insert certain components or other HTML elements

  • inside this section of the page.

  • We would just update our data, update the state of our components

  • in order to say this value of the state should take on this new value.

  • And React would take care of the process of updating the user

  • interface to reflect whatever it is was contained inside of our state.

  • So we're going to pick up where we left off,

  • continuing in with the morning project.

  • So there are a number of different ways to solve

  • the problem in this morning's project.

  • I'll show you a couple of them now just to give you

  • a sense for the different approaches, the way

  • that you can use React to implement some of these features.

  • And then I'll introduce what the afternoon project will be.

  • And the main focus of the afternoon lecture

  • is just going to be thinking in React, thinking

  • about what components we need, what state we need,

  • how we might set up an application.

  • And then we'll actually go ahead and try and implement the afternoon project.

  • So let's go ahead and get started with the to-do list application, where

  • we left it off this morning.

  • So as of earlier this morning, we had a to-do list application

  • that had a list of tasks, whereby I could type in a task, task one,

  • and I click Add task.

  • And that allows Add tasks to show up in this list.

  • But of course, task one is still listed here in the input field.

  • How do I get rid of task one from the input field?

  • Going back to this morning, how would I remove task one from the input field

  • when I click Add task, clearing out that field?

  • Which function needs the change?

  • Yeah?

  • AUDIENCE: Send the value of input to empty string.

  • BRIAN YU: Good, set the value of input to empty string.

  • And where should I do that, in which function?

  • AUDIENCE: In add task?

  • BRIAN YU: Great, in add task.

  • So when I add a new task, I want to do two things.

  • In addition to taking my tasks array and updating it, filling it

  • in with the new thing, I also want to say, all right

  • whatever the value of the input field was before, just

  • go ahead and set that equal to the empty string, because I

  • want to clear out the input field as soon as I click the Add task button.

  • So I'll go ahead and refresh this, try this out.

  • I type in task one, click Add task.

  • And all right, great, the task shows up and this field clears itself out.

  • The other thing that we ask you to do this morning

  • was show a count of how many tasks are currently in the task list.

  • And someone else tell me how you did that.

  • What did you change and what code did you have to write?

  • I know many of you did this.

  • So how did you do it?

  • Show how many tasks I currently have.

  • Yeah?

  • AUDIENCE: Use .length.

  • BRIAN YU: Yeah, if you have an array in JavaScript, and you use .length,

  • you can get at the length of the array, the number of elements in the array.

  • And so if I wanted to display the number of tasks that I currently have,

  • I could just say number of tasks.

  • And then using curly braces, again, to mean plug-in some JavaScript value

  • here, where are my tasks stored?

  • What would I type?

  • In the state?

  • OK, this .state.tasks.

  • And then .length to get at, OK, here is the number of tasks that I actually

  • have as of this moment inside of my application state.

  • So I refresh that.

  • And now I see, OK, tasks, number of tasks is zero.

  • But as soon as I add a first task here and click Add task,

  • multiple things happen.

  • The list updates.

  • We also see that this clears out and number of tasks

  • updates to reflect the length of that array.

  • Questions about any of that?

  • All right, so the challenging piece of this, of the morning project

  • was really the deletion of tasks.

  • How do you take a task in this list of tasks

  • and delete it so we no longer have it?

  • And so let's take a look at how we implemented that.

  • We'll scroll up to the top.

  • For every list item, when we map over all of our tasks, right now

  • we just have a list item that lists the name of the task just like this.

  • If I wanted to do more than that, well, I could just add to this list item.

  • In addition to just having a task, I'll also have a button, for example.

  • And that button we'll just call delete.

  • And let's leave it at that for now, just putting a button there.

  • Right now the button doesn't do anything.

  • But let's at least see it so we can visually

  • see what that button looks like on the to-do page.

  • We'll refresh the page.

  • OK, number of tasks is zero.

  • And I could say, OK, task one for example, add the task there.

  • And all right, great, we see the Delete button.

  • We also see task 1 show up next to it.

  • They're a little bit close together.

  • So you might try and modify some of the spacing of things,

  • the margin around the button, for instance,

  • in order to give yourself more space if you wanted to.

  • But of course right now, even if I have multiple tasks, I can click Delete

  • and the delete buttons aren't doing anything just yet.

  • So how do we actually delete a task?

  • Well, we probably want some function that's

  • going to take care of deleting a task.

  • And so one way to do this might be to say, all right, delete task.

  • And I saw people approach this in a couple different ways.

  • So I'll show you both ways that you could

  • have tried to approach this problem.

  • One way would have been to say, all right, delete task.

  • Well, this is a function that's going to take

  • some argument, some like index for which task

  • it is that I actually want to delete.

  • And the logic there would be, well, all right,

  • when I click on the button, button on-click.

  • Let me go ahead and delete that specific task corresponding

  • to index I, which is the index I'm currently on.

  • So you might have tried to write something like this .deletetask.

  • And then in parentheses, you might have thought all right,

  • let me put in the variable I there.

  • Now, this doesn't quite work.

  • Does anyone know why this doesn't quite work?

  • It's a little bit subtle.

  • Yeah?

  • Exactly.

  • Exactly.

  • This on-click attribute should take as its argument a function.

  • And the idea is that that function will not

  • be called upon until we actually click on the button.

  • But right now what we're passing into on-click

  • is the result of calling a function.

  • The fact that you see parentheses after this function

  • means that we're actually calling the delete task function, which

  • is not what we want to do.

  • What we want to do is we want to pass the function

  • into the on-click attribute, but not actually call

  • the function until we click on it.

  • So a couple ways to potentially handle that, the simplest way might

  • have been just to wrap this whole thing inside of a function, where, oops,

  • undo that.

  • What did I just do?

  • Delete task takes an index.

  • I think I accidentally deleted that.

  • And instead of saying on-click equals this .deletetaskI.

  • We can wrap this entire thing in a function

  • as simply as adding an extra set of parentheses and an arrow.

  • Remember, that arrow syntax creates a function.

  • And so we've created a function that doesn't take any arguments,

  • but that when it's run, will actually perform the deletion of the task.

  • So this is one way to do things.

  • And so now what exactly does the delete task function need to do?

  • Well, it needs to remove the task at this particular index.

  • So I'm going to say this .setstate.

  • And the new state is going to be a function of the old state.

  • I'm going to take the old state, modify it by removing the task,

  • and then returning the new state.

  • So this is going to be a function that takes the old state.

  • And then what do we need to do?

  • Well, the first thing we need to do is get a copy of that list of tasks.

  • Because I can't modify the state itself, as we mentioned before,

  • you should never modify the state directly and react.

  • So I'm going to create a variable called Tasks.

  • And that's just going to be equal to this .state.tasks filled into a brand

  • new array.

  • So I create a copy of that list, save it inside of a variable called tasks.

  • And then I want to remove whatever is at index I.

  • And so as we've decided this morning, the way we would do

  • that is by saying tasks.spliceindex1.

  • In other words, saying remove whatever is at index--index.

  • And how many things should I remove?

  • 1.

  • Now, the return value of splice is going to be the array that was removed,

  • that was spliced out of the tasks array.

  • But tasks itself, as a variable, is going

  • to be the original array with whatever we wanted to remove removed.

  • So there's a slight nuance there that I saw a couple people

  • struggle with a little bit.

  • Just know that what splice of returns is different than what

  • the actual value of the tasks variable is.

  • And now that I've spliced out whatever was an index,

  • I can now use this variable tasks to be the new list that has the thing

  • index--index removed from it.

  • And so this is a function.

  • So the last thing I need to do now is return something.

  • And I'm going to return, all right, what should the new tasks be?

  • It should be just this variable called tasks.

  • And so this is a slightly more complicated set state function.

  • In particular, it's more complicated because it's

  • really more of a full fledged function where

  • I'm defining variables, and manipulating those variables,

  • and returning some value.

  • Their add task meanwhile, was a much simpler function,

  • whereby I could just say I didn't need to create any new variables.

  • All I needed to do was say we start with this state

  • and we're just going to immediately return

  • this object that represents the changes that we're going to make to the state.

  • And so I'd just take a look at Add task and Delete task,

  • and see if you can get an understanding for the differences between the two.

  • All we need to do in Add task is add to this tasks array.

  • But in Delete task, we really need a little bit of extra logic

  • to copy the array, remove something from the array,

  • and then return what the new object should be.

  • Questions about anything so far?

  • This was one way to implement things from this morning.

  • Yeah?

  • AUDIENCE: [INAUDIBLE]

  • BRIAN YU: If you're getting an error about-- so

  • I would first check to make sure that your curly braces in parentheses

  • match the curly braces in parentheses that I have here.

  • You might be using the keyword in a place in your code

  • that you're not allowed to use it.

  • But if you're still getting that error after that,

  • it's probably some syntax thing that we can

  • take a look during the project time.

  • Yeah?

  • AUDIENCE: Why wouldn't we use this .setstate for delete task?

  • BRIAN YU: We are using this .setstate for delete task.

  • Yeah?

  • AUDIENCE: [INAUDIBLE]

  • BRIAN YU: In the Add task function--

  • so this is a little bit of a nuance of the way

  • that arrow functions in JavaScript work--

  • if all we're doing in a function is taking some input

  • and immediately returning its output without anything else,

  • I can just say state as the input, arrow,

  • and then immediately give whatever the thing I'm returning

  • is, which is this JavaScript object.

  • And so if that's all the function is doing,

  • there's no additional logic, no loops, no variables, or anything like that,

  • then you don't need to explicitly say return.

  • Whereas in Delete task, the function is a little more complicated.

  • I need variables.

  • I need to manipulate those variables.

  • And so at the end of that, I do need to explicitly say return something.

  • Yeah?

  • AUDIENCE: What is .something, [INAUDIBLE]..

  • BRIAN YU: This is because I'm calling this .setstate.

  • And the function that I'm passing into this .setstate is this entire thing.

  • This function.

  • And so this function, what it's returning ultimately

  • is a new set of tasks that is going to become the new state.

  • Yeah?

  • AUDIENCE: Shouldn't this be just state, not .state?

  • BRIAN YU: Oh, you're right, this should just be state, not this .state.

  • Thank you.

  • That's a good catch.

  • Yeah?

  • AUDIENCE: If you put the variable conts task outside of this .setstate,

  • will that work?

  • BRIAN YU: If you put conts task outside of this .setstate and you do something

  • like this .state.tasks here, this will work in this example,

  • but it suffers from the potential for the race conditions that we talked

  • about earlier where if we're basing the new state of the old state,

  • we should really be using the state input to the set state function.

  • So in this example, it will work fine both ways.

  • But good design habit to get into the habit of doing things this way.

  • Other things?

  • I think there are other questions.

  • Yeah?

  • AUDIENCE: I think we probably have [INAUDIBLE]..

  • BRIAN YU: Yep.

  • AUDIENCE: So we decided to practice using [INAUDIBLE]..

  • BRIAN YU: Right, we're wrapping this delete task inside of a function

  • so the delete task isn't run until we actually call this function.

  • And this is a common way in programming to delay

  • the evaluation of some expression is just to wrap the whole thing

  • inside of a function.

  • And only when you call that function will the value actually be evaluated.

  • So this will work.

  • This actually is a little bit inefficient in the sense

  • that every time I generate a button, it's

  • going to regenerate one of these new functions.

  • And so I'll show you another way of doing the exact same thing.

  • Totally fine to do this, but if you're looking for really trying to optimize

  • your react code, I'll show you one other method of achieving the same goal,

  • where instead of wrapping this whole thing in a function,

  • I can just use this .delete task.

  • Again, I'm not calling the function just yet.

  • Delete task is going to be called eventually.

  • But this time delete task is not going to take an index as its argument.

  • But instead, what I'm going to do is I'm going to associate

  • some data with this button.

  • In the same way that in JavaScript before we could attach data attributes

  • to HTML elements, we can do the same thing here,

  • because this is just JavaScript, where I can say buttondata-index is going

  • to be equal to I. Remember, data attributes

  • are a way of adding information data that we

  • care about to our HTML elements.

  • And here I'm just adding I as the data attribute of this particular button.

  • So now when someone clicks on delete task,

  • this delete task is going to take an event as an argument,

  • because we're going to need access to the event.

  • Because if we try and get at the event.target,

  • that's going to give us the button, the button that we clicked on.

  • And if we access the data set for that button, the data attributes of it,

  • and get at the index of that dataset, what that's going to do

  • is it's going to give us access to the index of the button.

  • It's going to take that button, get at its data properties,

  • and get at the field called data-index.

  • And that will give us access to whatever the index of this particular row

  • happens to be.

  • And so this will behave the same way, where I can say task one and task 2.

  • And if I delete task two, all right, great, I'm left with just task one.

  • And the number of tasks decreases from two to one as well,

  • because React is dynamically going to update the DOM with any changes

  • as a result of changing the state of my components.

  • So two different ways of achieving the same thing.

  • I'm just showing you both, because they're

  • both the types of things that you might see in the world of programming

  • in React.

  • Questions about anything?

  • Yeah?

  • AUDIENCE: If we do it this, it's like a data attribute.

  • For the index we use just value.

  • Does that work for you?

  • BRIAN YU: Yeah, you can use other attributes as well

  • and access as attributes, and that would probably work.

  • Yep?

  • AUDIENCE: [INAUDIBLE]

  • BRIAN YU: So again, delete task, just to review,

  • is getting the index from the data attribute of the button.

  • Then updating the state.

  • And to update the state, we take the original tasks,

  • splice out whatever was at index I, and then return the new tasks.

  • And a small bit of JavaScript shorthand, which you don't need to know,

  • but might be useful sometimes, is if ever you have a situation where the key

  • of a JavaScript object has the same name as the value,

  • you actually don't need to say tasks:task.

  • If you just say tasks, that will implicitly just mean the key in a value

  • have the same name.

  • And so this is just a little bit of shorthand for doing the same thing.

  • But no need to worry about that if you don't want to.

  • Yeah?

  • AUDIENCE: Are you worried about this being inside set state?

  • Is it better to be outside?

  • BRIAN YU: This, it doesn't matter whether this

  • is inside or outside of set state, because it's not

  • dependent upon the state variable.

  • So you could calculate the index wherever.

  • So long as you have access to the same event,

  • the data index value is always going to be the same.

  • So it doesn't matter if it's inside or outside of the function.

  • Other things?

  • Yeah?

  • AUDIENCE: [INAUDIBLE]

  • BRIAN YU: Yeah so these events you can think of as the same sorts of events we

  • were dealing with in JavaScript before, where when I say button on-clicked

  • equals this .addtask task for instance, then the event that is happening is

  • the click event.

  • And the target of the click event is the button

  • that I actually use to do the clicking.

  • And so the way this works in the case of delete task is that when I call this

  • .delete task, it's going to be provided with the click event,

  • the fact that I clicked on a button.

  • And if I access event.target, that's going to give me which button

  • I actually clicked on to do the deleting.

  • So if I have 10 buttons, each of which has a different data-index property,

  • then if I go to event.target and look at what data-index is,

  • I'll know which button I clicked on and so

  • I'll know which task I should remove from my list of tasks.

  • Yeah?

  • AUDIENCE: So [INAUDIBLE],, but basically what I did is after delete task,

  • hit the button I, I used the index for I.

  • And then down at the function delete task, I used [INAUDIBLE]..

  • BRIAN YU: Yeah, so I showed two possible ways of doing it.

  • And so one way is to do it the way that you're describing it.

  • Yep.

  • Yeah?

  • AUDIENCE: Can you explain why the code is [INAUDIBLE]..

  • BRIAN YU: So we only need event as an input to the delete task

  • function because we need to use event to access the target, namely the button

  • that we clicked on, because we need the button to get

  • at the data attributes of the button.

  • In the first method that we used, we didn't

  • need to access any data attributes.

  • So it didn't actually matter to have access

  • to the button that was clicked on, because delete task was already

  • being provided with the index.

  • So we didn't need to access the event.

  • Other things?

  • Yeah?

  • AUDIENCE: Why is this way more efficient?

  • BRIAN YU: This way is slightly more efficient because in the other way when

  • we created a new function for each of the on-clicks where we did curly braces

  • arrow, this .deletetaskI, that would be a situation where every time

  • it's creating a new button, it's creating a brand new function in order

  • to put in on-click.

  • Whereas here, I'm just using the same this .delete task function.

  • And so it's just a slight efficiency benefit

  • that's really not a big deal here.

  • Though in larger applications, you might imagine

  • it could start to become a big deal.

  • In this case, it's probably fine just to avoid over optimizing.

  • And either way is OK.

  • All right, so really the best way to get good at React

  • is to start writing applications in React.

  • And we'll do some of these together.

  • And so that's what the goal of today and tomorrow is going to be.

  • React is a new way of thinking about writing programs.

  • It's a new way of working with programs, and thinking

  • about state and applications, and how to modify the user interface.

  • And so we'll go through a couple now slightly larger projects

  • that will step through the beginning parts together

  • to give you a sense for the types of things that you can do with React

  • and how to think about designing an application in React.

  • And so the application we're going to build now

  • is going to be a similar analog to the quiz application we had before.

  • We're to create a flash card application.

  • And so the flash card application is ultimately

  • going to look something like this.

  • When it's done, it's going to have a couple different modes.

  • It will have a card Editor.

  • And a card Editor basically allows me to input terms and definitions

  • into the cards.

  • So maybe I want to create addition flash cards, for example,

  • to help me practice for the addition game that we did earlier this morning.

  • So I do a front side and a backside.

  • A front and a back for the card.

  • Press Add card.

  • And all right, that adds to this list of cards.

  • I can add other front and back and more as well.

  • If I wanted to delete a card, I can delete a card too.

  • And then if I want to, once I'm satisfied with editing these cards,

  • I can switch to the card Viewer.

  • And the card Viewer is going to show me a card.

  • If I click on it, it'll flip it over so I can see the back.

  • If I click on it again, it will flip back to the front.

  • And I can click on New card to get me a new card.

  • Get me the next card in the list.

  • When I click on that, it'll flip over, show me

  • the back of the card, so on and so forth.

  • I can switch back to the Editor.

  • I can switch back to the Viewer.

  • So this is the application we're going to build.

  • And it seems a little bit complicated.

  • But let's try and break it down into components.

  • And what's the first thing that you might notice?

  • What is this interface at least remind you

  • of in terms of things we may have done today?

  • Yeah, it reminds you very much, hopefully,

  • of the tasks management application that we did this morning of just

  • creating a to do list of tasks.

  • This is effectively exactly the same thing,

  • except instead of a task just having one field for the task,

  • the task has a front and it has a back.

  • It's two input fields instead of one.

  • So this interface is probably largely going

  • to resemble what it is that you did earlier this morning.

  • We just now need to add a Viewer to it as well to be able to view those cards

  • and flip the cards over from front to back.

  • So questions about the goal-- what we're trying

  • to achieve in writing this application?

  • All right, let's go ahead and dive right in and actually start

  • working on the application.

  • So we'll go ahead and create a new file.

  • We'll call it flashcards.html.

  • And I'll go ahead and start with just going ahead and copying

  • the to-do app just so we get the basic structure of it.

  • But I'm going to completely empty out what's inside of class at, at least

  • for now.

  • We'll give it a title of flashcards.

  • And all right, let's actually think for a moment.

  • If I say term and definition, what are the different components

  • that I might divide my application into?

  • Process to different components of the application?

  • There are at least two big components of the application,

  • distinct parts of the application that behave differently.

  • The Viewer and the Editor.

  • Great, I have this Editor right here that is a place where I can edit tasks,

  • add tasks to them.

  • And I also have some Viewer that lets me view all of the cards

  • that I currently have inside of my flash card table.

  • And so those are probably at least two different components

  • that are going to behave differently.

  • So one thing I might do right away is say, all right, I'm

  • going to have a class called card Editor that is going to be a component.

  • And for now, let's go ahead and render that just by returning a div that says

  • this is the Editor.

  • So all right, I have a class called card Editor.

  • And all the card Editor is going to do is say this is the Editor, at least

  • for now.

  • In addition to that, I probably also want

  • a class called card Viewer that going to extend React.component.

  • And we're going to render it by returning for now this is the Viewer.

  • So all right, we have two classes, card Editor and card Viewer.

  • And right now they just say this is the Editor, this is the Viewer.

  • And I can test this, make sure it works by inside of my app.

  • In order to render the app, let's for now just

  • return a card Editor and a card Viewer.

  • We'll load both components into the application, one on top of the other,

  • just to see how this looks.

  • I'll go ahead and open up flashcards.html.

  • And all right, great, this is what I see.

  • I see the card Editor that just says this is the Editor.

  • And I see the card Viewer that just says this is the Viewer.

  • But of course I don't want to display both the Editor and the Viewer

  • at the same time.

  • So let's step by step start building up this application.

  • What is a piece of state that I need inside of my application?

  • Yeah?

  • AUDIENCE: Whether you're on Editor or Viewer?

  • BRIAN YU: Exactly, whether I'm on the Editor

  • or whether I'm currently on the Viewer.

  • So it's some notion of, like, what the current mode of the application is.

  • And so I could implement this by saying, all right,

  • let's create a constructor that takes props, super props just

  • to make sure that the component gets its props correctly.

  • And let's give some state to this application.

  • We care about knowing whether or not we're in the--

  • so we could do this state in a number of ways.

  • We could say, all right, let's add a mode property to the state

  • where the mode could start out being Editor, for example.

  • And then mode could be any number of different things.

  • But right now there are only two different modes.

  • So I'll just say Editor is true.

  • We'll have a piece of the state called Editor.

  • By default, it is true.

  • So by default, I'll see the Editor.

  • And if ever the value changes, I'll show something else.

  • And so, OK, what do I do now?

  • Well, inside the Render function, we'll say something like if this

  • .state.editor, implicitly if this .state.editor is true,

  • then what do I want to do?

  • Well, I want to return a card Editor.

  • And otherwise else, I want to return a card Viewer.

  • And I go ahead and delete this unnecessary return here at the bottom.

  • So here is the render code for my app.

  • I'm basically checking whether this .state.editor,

  • should the state of my application be showing the Editor or not showing

  • the Editor?

  • If I am supposed to show the Editor, then

  • go ahead and return the card Editor.

  • Otherwise, go ahead and return the card Viewer.

  • So if I load the page now, refresh flashcards.html,

  • I just see this is the Editor and I have no way of accessing the Viewer.

  • I only see the Editor.

  • Questions about anything?

  • BRIAN YU: All right, so let's take the next step.

  • I can have the Editor.

  • And now I want some way of flipping back and forth between Editor and Viewer,

  • between Editor and Viewer, going back and forth between these two components.

  • And so I probably need some way inside of my app

  • to change the value of this Editor piece of state.

  • I want some function that is going to change whether Editor is true or false.

  • So I'll go ahead and add a new function.

  • And the new function, we'll call it switch mode.

  • Switch mode is going to be a function that

  • takes me from Editor mode to Viewer mode, and from Viewer mode

  • to Editor mode.

  • So it's going to be a function.

  • In order to do that switch, I need to set the state to something.

  • So I'm going to start with some starting state.

  • And what is the new state?

  • What goes in here to switch the mode of my application?

  • Yeah?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: Yes, so we have inside of this .state a notion of where we are.

  • If Editor is true, we should be on the Editor.

  • If Editor is false, we should be on the Viewer.

  • So yes, good, we need that.

  • And, yes?

  • AUDIENCE: Editor, [INAUDIBLE].

  • BRIAN YU: Yeah, exactly.

  • Editor, we're going to change the value of Editor

  • to exclamation point for not state.editor.

  • So whatever the value of state.editor was before,

  • let's take whatever is not that, so if it was true, it's now false.

  • It was false, it will now be true.

  • And that will be the new value of Editor, for example.

  • So all right, now what I need is I would like some way for maybe the Editor

  • to have a button and the Viewer to have a button that

  • will result in switching the mode of the application, for example.

  • And so how do I go about doing that?

  • Well, there are a of ways to do it.

  • I could put the button directly in the app right

  • here and just have a button at the bottom that's going

  • to say, like, switch mode or something.

  • Another way to do it would be inside of card Editor for instance,

  • let's go ahead and add maybe a horizontal row.

  • HR just creates a horizontal row, a line, across the page effectively,

  • and a button that says go to Viewer.

  • And in the Viewer, go ahead and create a horizontal row and a button that says

  • go to Editor.

  • So OK, I have the task Editor that has a button that says go to the task Viewer.

  • And I have the Viewer that has a button that says go to the Editor.

  • If I refresh the page, it says this is the Editor

  • and there's a button that says go to Viewer.

  • Of course if I click on that, nothing happens.

  • Nothing happens because I haven't specified

  • what should happen when I click on this Go to Viewer button.

  • What I would like to happen, if I click on the Go to Viewer button in the card

  • Editor component, is that it calls the switch mode method of my app component.

  • But there is a slight problem, what's the problem?

  • Why can't I just say switch mode?

  • Yeah?

  • AUDIENCE: They're in different classes.

  • BRIAN YU: They're in different classes.

  • Exactly.

  • Switch mode is a method inside of the app component,

  • but I want to be able to call it from the card Editor component.

  • They're in different components, but I want

  • the card Editor to still be able to call a function inside of the app.

  • So how would I do that?

  • Any thoughts on how I might solve that problem?

  • The function is inside of the app and I would

  • like to give it to the card Editor.

  • In other words, pass information about that function or pass the function

  • itself to the card Editor so the card Editor can use it.

  • Thoughts on how to solve that problem?

  • We saw the solution this morning.

  • Yeah?

  • AUDIENCE: Could you have it extend the app?

  • BRIAN YU: Could you have it extend the app?

  • Yes, you could.

  • Generally, React doesn't recommend that you have different classes extending

  • other classes, because generally the classes want

  • to do pretty distinct things.

  • But that could be one solution to the problem.

  • Yeah?

  • AUDIENCE: You can use a property.

  • BRIAN YU: You can use a prop.

  • Great.

  • And that's going to be the solution we're going to use.

  • We learned already that if I want to pass information into the card Editor,

  • I can add props to the card Editor.

  • I can say, all right, give the card Editor a switch mode property that is

  • equal to this .switchmode.

  • Remember, I'm inside of the app component right now.

  • And when I load my card Editor component,

  • I would like to provide it with a property called switch mode.

  • And that is equal to the this .switchmode function.

  • I'm passing this function into the card Editor component

  • so that the card Editor component can use that function.

  • It can be helpful here to think about the hierarchical structure

  • of the application.

  • We have the app, which is the big container, inside of which

  • is the card Editor.

  • And the app is going to give the card Editor this, this.switchmode function

  • so that the card Editor can now use that function.

  • Yeah?

  • AUDIENCE: Let's say you have a function that every single last [INAUDIBLE]

  • would use.

  • If you could find that function, [INAUDIBLE]..

  • BRIAN YU: That's a good question about if you

  • had a lot of components that were all trying to take advantage of functions

  • that are modifying the state, with just React,

  • you'd have to do something along these lines.

  • Although, there are other libraries.

  • In particularly, a library called Redux, which is quite popular,

  • which helped to make that process easier and can have some useful abstractions

  • for simplifying that process.

  • We're not going to get our chance to talk about it this week.

  • It's a little beyond on the scope of the class.

  • But Redux is what you should look into if that's something

  • that you're trying to do.

  • So OK, I provided the switch mode property into the card Editor.

  • And now inside of card Editor, what should button on-click do?

  • What is the function that I need to call when the button is clicked?

  • AUDIENCE: This .prop.

  • BRIAN YU: This .props.switchmode.

  • Great.

  • I want to access the switch mode function,

  • and the switch mode function was provided

  • as one of the props of this card Viewer component.

  • So when I click on the button, it should call this .props.switchmode.

  • I'll go ahead and refresh the page.

  • It says this is the Editor.

  • I click on and go to the Viewer, and, uh-oh, something went wrong.

  • Why did that not work?

  • This .props.switchmode.

  • AUDIENCE: [INAUDIBLE]

  • BRIAN YU: What did I not do?

  • I didn't edit-- oh, I edited the Viewer class.

  • Yes, thank you.

  • I'll go ahead and make the same change to the Editor class button.

  • When you click on it, unclick this .props.switchmode.

  • Thank you for that.

  • OK, now if I refresh the page it says this is the Editor.

  • If I click on Go to Viewer, it switches.

  • Now I'm on the Viewer, I click go to Editor

  • and why did that one not work now?

  • Oh, why did that one not work?

  • Why did card Viewer not work?

  • Anyone know?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: It has this .prop.switchmode.

  • But what did I forget?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: I need to give it the prop.

  • Exactly.

  • So I have card Viewer, but I didn't provide it with a switch mode prop.

  • And so I need to say card Viewer and switch mode prop is going to be equal

  • to this .switchmode for example.

  • So now I have the Editor.

  • I can switch to the Viewer and hopefully I can switch back to the Editor.

  • You can go back and forth between the two just

  • by changing the state of the application.

  • Questions about anything so far?

  • All right, so let's go ahead and try and build out this Editor.

  • We'll try and build the Editor together.

  • And then I'll leave most of the Viewer to you

  • to try and figure out if you'd like to.

  • So inside of our application, we currently

  • have some state that indicates whether we're on the Editor mode

  • or on the Viewer mode.

  • We're also going to need some state about, like,

  • what the current cards inside of my collection of flash are.

  • Like, what are the cards, what's on the front, what's on the back of them.

  • And an interesting question comes about of where should that state live,

  • should that state be inside of app, should it be inside of card Viewer,

  • or should it be inside of card Editor?

  • Each is a component, each can have its own state, which component should

  • maintain the list of all of the cards?

  • What do we think?

  • Yeah?

  • AUDIENCE: [INAUDIBLE] Editor.

  • BRIAN YU: Yeah, exactly.

  • We want the state about all the cards to be accessible to both

  • the Editor and the Viewer.

  • So it probably makes sense to put the state

  • about what all the cards are inside of the app,

  • give the state access to cards, which by default will just be an empty list.

  • That way we can provide that information to both the Editor and the Viewer,

  • and both can have access to that same state.

  • And this is an idea you'll commonly see in React called Lifting State Up

  • that you might think, OK, the card Editor needs to have access

  • to state about all of the cards.

  • But if we lift the state up out of the card Editor and into the application,

  • then anything the application uses, any of its components

  • can have access to that same piece of state.

  • Questions about that?

  • All right, let's go ahead and try and build the card Editor now.

  • So importantly, when I have the card Editor,

  • the card Editor needs to know what the cards are.

  • So I'm going to go ahead and provide the card Editor

  • with an additional property.

  • And that property is just going to be called cards, I suppose.

  • And cards should be equal to what value?

  • AUDIENCE: This .state.cards.

  • BRIAN YU: This .state.cards.

  • Great.

  • This is inside of my app component, and so when I have the card Editor,

  • I want to provide it with cards, and its value should be this .state.cards.

  • You'll notice this line is starting to get a little bit long.

  • So it's often common in React paradigms to see each

  • prop to be on a new line of its own.

  • And so you'll often see something that looks a little something like this,

  • where I'll return card Editor.

  • Cards is equal to this .state.cards.

  • And I'll also give it access to switch mode.

  • And I'll do the same for card Viewer, give it access to cards in this

  • .state.cards, give it access to switch mode for the card Viewer.

  • They both need access to that same information.

  • So now let's go ahead and build our card Editor.

  • Right now all it does is just say this is the Editor.

  • Let's add some HTML to it.

  • At this point, I can just write HTML and declaratively describe

  • what it is that I want the Editor to look like.

  • Well, it should be in H2, maybe, that says card Editor.

  • You can make it in H1 also, make it a little bit smaller.

  • And underneath the card Editor, I want there to be a table.

  • A table where I have rows and columns, a way

  • to show the front and the back of each card,

  • and then a button to delete each card.

  • So underneath this, I'll say table.

  • The table is going to have a table head and a table body,

  • either just HTML just to make it easy to separate the heading of the table

  • from the body of the table.

  • And the heading of the table is going to have a table row, just

  • a row along the top of the table.

  • And that row is going to have three columns.

  • The first column will be, OK, this is the front of the card,

  • this is the back of the card, and here's a button

  • to delete this particular card.

  • So I have a row at the top of the table, front, back, delete.

  • And inside the body of the table, here is now where all of the rows

  • are going to go.

  • And oftentimes an easy way of just doing this is by declaring a variable

  • and then we can go back and actually declare it.

  • I'll just say curly braces rows, meaning the rows of the table

  • are going to go here.

  • Now, rows is a variable I haven't yet actually defined.

  • And so I can define a variable up at the top of the render function.

  • I can define a variable called rows, which

  • is going to be equal to, all right, well how am I going to get all the rows?

  • Well, this is going to look very similar to how

  • we got that unordered list of items inside of our task list.

  • What I'm going to say is, where are the cards stored,

  • this .state or this .props?

  • Props.

  • Great.

  • Why props?

  • Why are the cards in the props?

  • Yeah?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: Yeah, so in app when we were creating the card Editor,

  • we passed in cards as one of the properties of the card Editor.

  • And so now in order to access those cards, we can say this .props.cards.

  • And then I want to again map over each of these cards,

  • doing something with each card, in particular,

  • creating a table row for each one of them.

  • So I'll say map and we'll go map over every card,

  • and also give it an index variable I so we have access to which card

  • it actually is.

  • And we're going to go ahead and return a table row.

  • Each card is going to correspond to a single table row.

  • We'll give that table row a key, because React

  • likes to have anything that's iterated over a key that is unique.

  • And we'll give it three cells inside of that row, a front, a back,

  • and a Delete button.

  • For the first cell, we'll say card.front.

  • We'll make each one an object.

  • Right now we haven't actually seen how we create a new card yet,

  • but this is how we would print them out for instance.

  • Then we'll have another one that says card.back.

  • And then we'll have another one that's just a button that says delete.

  • So OK, I've defined rows, which is going to be taking all of the cards

  • and mapping over them, looping over all of the cards.

  • And for each card, creating a new row.

  • That row has three columns, one column to represent the front of the card, one

  • column to represent the back of the card,

  • and one column to be a button that I can click on to delete the card.

  • Very similar again to the task management

  • example from earlier this morning.

  • So hopefully you can see the parallels that are there as well.

  • Take a look at the example from this morning at the map

  • that we were doing over the list of tasks.

  • And you'll see a lot of similar code that's being used here,

  • a lot of similar ideas in React.

  • To test this out, rather than starting cards off with an empty thing,

  • I can just for the sake of example give it, OK, front is going to be test front

  • and back is going to be test back.

  • And we'll go ahead and add a second one, front equals test 2 front,

  • back is test 2 back.

  • Just giving it some default state that I'll delete eventually,

  • but that'll be useful for now until I have the ability

  • to add cards to be able to see what are the cards

  • that I currently have inside of my application.

  • If we open up flashcards.html, all right, great,

  • we see now that we have a card Editor and we have

  • three columns, front, back, and delete.

  • The Delete button doesn't do anything yet.

  • But at least now I can see the contents of my Editor.

  • What questions do you have so far with what we've done just now?

  • We just created an Editor where we can see the contents of whatever

  • flash cards we currently have.

  • I'm going to add some CSS code just to make this look a little bit nicer.

  • If we go up to the top of our HTML page, this is very similar to CSS code

  • I've added before.

  • So we'll go through it a little bit quickly.

  • But feel free to stop and I'll post this code once we move into project time

  • if you want to take a closer look at it.

  • But basically tables, and table data cells, and table heading cells,

  • I should probably change the headings to table headings.

  • I'll actually do that now.

  • The headings should probably be th instead of td.

  • Effectively, this isn't changing a whole lot.

  • But it helps to make sure that HTML knows that these

  • are in fact headings for the tables.

  • So the table, table data cells, and table headings,

  • those will have a border that is a one pixel solid block border for example.

  • And so, OK, what does that look like?

  • All right, we have a one pixel solid black border around each of the cells.

  • I don't like the fact that there is two lines between a lot of the information.

  • And so in order to collapse that inside of table,

  • we can use the border collapse is collapsed CSS property.

  • That'll basically take all of the neighboring sections of the border

  • and just collapse them down.

  • And now this feels a little bit cramped.

  • So I'd like to add a little bit more space

  • in between these various different elements.

  • So for table data cells and table headings cells,

  • let me add 10 pixels of padding on the inside of each of those cells.

  • And that will be space on the interior of the border such

  • that when I refresh the page now, all right, this looks a little bit better,

  • at least a little more aesthetically pleasing.

  • And you should feel welcome to mess with the CSS all you want.

  • You can add colors, change the border if you would like to.

  • You can center the table if that looks better for you.

  • So feel free to make any changes to the style and the aesthetics of the code,

  • and also the functionality of the code if you choose to do so.

  • Questions about anything thus far?

  • All right, so we have this table.

  • And now in addition to having a table, I also

  • need some way of adding a new card.

  • So I'll go ahead and say line break here.

  • And I'll have an input whose name is going to be front

  • and that'll be it for now.

  • And an input whose name is going to be back,

  • and actually I'll give each of them a placeholder at least.

  • I'll call it front of card and place holder back of card.

  • So we have two input fields now.

  • And I'll have a button that says Add card.

  • So I have two input fields, one called front, one card called back.

  • And a button that's going to add a new card to this list of cards.

  • Press Refresh.

  • Some error somewhere.

  • I executed closing tag for input.

  • Oh, I need a flash to close these input tags.

  • All right, now I have a card Editor.

  • It's got a table.

  • And I also have a place where I can type in the name of the front of the card,

  • then whatever goes on the back of the card.

  • And then a button to actually add that card to this table.

  • So now what state do I need to keep about the card Editor?

  • What can change about the card Editor?

  • Similar in spirit to what we had with the to-do list,

  • what are two pieces of state that I need?

  • Yeah?

  • AUDIENCE: The input.

  • BRIAN YU: The input.

  • Yeah, what it is that I'm typing into the front field,

  • and what it is that I'm typing into the backfield,

  • that all needs to be state inside of my card Editor.

  • So I'll go ahead and create a constructor

  • that takes in some properties.

  • And I'll set the initial value of this .state equal to, all right, well,

  • the front of the card by default is just going to be empty.

  • And the back of the card is by default also just going to be empty.

  • And that's going to be the value of this .state.

  • If I scroll down to these two input fields,

  • this is the input field where I type in what goes on the front of the card.

  • This is the input field where I type in what goes on the back of the card.

  • The value of this input field is going to be this .state.front,

  • whatever it was on the front of the state.

  • And the value for the back, this .state.back.

  • I'm basically doing the same thing I did with all the input fields

  • before, just giving them a value so that I can later refer back to them.

  • Both of them need an on change property for what happens when I actually start

  • typing something into this input field.

  • And I'll just say this .handlechange as the name of the function for when I

  • actually start typing something into the front field or into the backfield.

  • For the back one, I'll do the same thing, onchange=this.handlechange.

  • And now let's write the handle change function

  • for what should happen when I start typing something into the input field.

  • And let me do that underneath at the end of the render function.

  • Handle change is going to be a function that takes its event.

  • And handle change is going to need to modify the state of the application.

  • And if I wanted to change the front of the value front inside of the state,

  • I could say this .state front is equal to event.target.value.

  • And that would change the front value.

  • And if I wanted to change what was on the back of the card,

  • I would have to do this .state back equals something else.

  • And so this is a little bit problematic, because I'm using this .handlechange

  • for both front and back.

  • I'm using the same event handler, this .handlechange for both of them.

  • And so I need some way of differentiating between these two event

  • handlers or saying, if I type something into the front input field,

  • then I want to change the value of this .state.front.

  • And if I type something into the back input field,

  • then I want to change this .state.back.

  • And so taking a look at these input fields,

  • is there any property that would be helpful for doing that?

  • Take a look on 63 and 64, any attribute these elements

  • have that would be useful.

  • Yeah?

  • AUDIENCE: You could somehow put the name [INAUDIBLE]..

  • BRIAN YU: Yeah, exactly.

  • I could somehow take advantage of the fact

  • that the name attribute of this input field is front

  • and the name attribute of this input field is back.

  • And I would like to if the name is front, set the state for front

  • to be event.target.value.

  • And if it's back, then set it to the back value.

  • And so JavaScript has some special syntax for doing this.

  • It's not something you've likely seen before.

  • But if I in brackets say [event.target.name],

  • well that's going to say whatever the value of event.target.name is,

  • let's go ahead and use that as the key.

  • And then event.target.value is going to go ahead and be the value.

  • So I can use a single handler to be able to handle

  • changing both the front and the back value of the state.

  • So something you might see from time to time,

  • this is common when you have multiple different input fields.

  • And rather than create a handle change front function and a handle

  • change back function, which would just be repetitive,

  • you can just have a single function that takes care of all of that.

  • Yeah?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: Yeah, so I could add logic.

  • I could equivalently do this by doing ifevent.target.name=front,

  • then do something, and else do something else, and that would work too.

  • And again in React as with almost any programming project,

  • there are many ways of achieving the same things.

  • So pick the thing that makes the most sense to you

  • is often the best piece of advice.

  • So all right, I have a card Editor now where

  • I can type something into the front of the card and something else

  • into the back of the card.

  • And clicking Add card right now does nothing.

  • So let me go ahead and write a function that is going to add a new card.

  • Now to add a new card, do I need to modify the state of card Editor, card

  • Viewer, or app?

  • Again, I have three components.

  • Which state do I need to modify to add a new card?

  • App state, right.

  • Because app, that class, is the one that actually

  • has cards inside the state of the application.

  • So we're going to need to add a Add card method to this application.

  • And the Add card method, we'll have it take two arguments.

  • We can choose what the arguments are.

  • But it's probably going to make sense for the Add card method

  • to take an argument for the front and an argument for the back.

  • And then do something with those two values.

  • So to add a card with a front equal to front and a back equal to back,

  • I can do this .setstate.

  • I want to update the state of my application.

  • And I want to set cards equal to, well, I

  • want all the existing cards plus my new card.

  • So I'll say ...state.cards to say fill in all of the existing cards

  • into the array.

  • And now I'd like to add my new card to this array

  • as well, where the front is going to be equal to front,

  • and the back is going to be equal to back.

  • So I'm updating the state, saying take the old state,

  • go ahead and fill in all of those cards, and then just tack

  • on to the end of the array a new object where the front of the card

  • is this variable front, and the back of this card is this variable back.

  • Yeah?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: Yeah, excellent point.

  • So I mentioned this a moment ago of JavaScript object shorthand,

  • because front's key and front's value are the same name,

  • they're both called front and back, they're both called the same thing,

  • this happens often enough in JavaScript that there's shorthand for it.

  • I can just say, front, back and it's just

  • going to assume the keys and values have the same name.

  • And so this will do exactly the same thing.

  • But yeah, that's the shorthand I can use.

  • Yeah?

  • AUDIENCE: Can you repeat [INAUDIBLE].

  • I'm a little bit confused with event.

  • Why does switch mode not take an event, if it also [INAUDIBLE]??

  • BRIAN YU: So why does switch mode not take an event.

  • Switch mode could take an argument called event.

  • JavaScript option has functions that have

  • these optional arguments that if you wanted to take an event argument,

  • it can.

  • But switch mode doesn't need to access the event.

  • Because the only thing the switch mode function needs to do

  • is take, whether it's on the Editor and the Viewer,

  • and switch it to the other Viewer, the other component that I have.

  • And so nothing about the event actually matters for the logic

  • of the switch mode function.

  • Whereas by contrast, handle change actually does care about the event.

  • The event of me changing the input field is important for two reasons.

  • One, I care about the event, because I care about the name of the input field

  • that I was typing something into.

  • Was I typing something into the front field or the backfield?

  • And depending on that, I want slightly different logic.

  • And it also matters because I care about what is it that I actually typed in.

  • Because what it is that I typed in should be the new value of either front

  • or back in the state.

  • So if you need to access the event for some reason,

  • usually to get at event.target, the thing that's triggering the event,

  • then you'll need the event parameter in the function.

  • But if you don't need the event, then it doesn't matter whether you include it

  • or not.

  • And so generally speaking, we won't include it.

  • Yeah?

  • AUDIENCE: For all the examples you've been doing [INAUDIBLE]

  • Is there a reason we're doing that instead of on the bottom click

  • maybe change the state of the [INAUDIBLE]..

  • BRIAN YU: So when the button is clicked, we

  • want to be able to just look at the state of the application

  • to be able to know what is the front value and what if the back value.

  • And so generally in React, any time something

  • changes in the user interface, that should be represented

  • by some underlying change in state.

  • In other words, if you know the state of the application,

  • what the value of this .state is for all of your components,

  • I should be able to tell you exactly what the page is going to look like.

  • And so that's just sort of part of the React paradigm.

  • Yeah?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: It depends on what you try to do with properties that you

  • don't provide in.

  • So if you try to use a property that you don't provide and just,

  • like, paste it into the HTML content somewhere inside your render function,

  • odds are it will work but just nothing will show up there.

  • But if you try and do any logic with it, like, if you try and call

  • or pass it in as arguments to functions or perform operations with it

  • and they don't exist, then you could start to get errors.

  • So you do want to be a little bit careful about that.

  • Other things?

  • OK, so we have this Add card function.

  • And in particular, this Add card function

  • is a function that our card Editor needs to have access to.

  • And so I'll go ahead and add to this card Editor.

  • The card Editor knows how to switch modes.

  • But the card Editor also needs to know how to add a card.

  • So Add card will be equal to this .addcard.

  • And now inside of our card Editor, when you click on the button, on-click,

  • I'll call this .addcard.

  • So this is a different Add card function.

  • But what needs to happen when I add a card in a Viewer?

  • Well, two things need to happen.

  • One is I need to actually call the Add card function in my props.

  • This .props.addcard, passing in whatever was in the front input field

  • and whatever was in the back input field.

  • And that information is stored in this .state.front and this .state.back.

  • And then what else should I do from a user experience perspective

  • if I type in something into the front input field, type in something

  • into the back, and click Add card?

  • AUDIENCE: Clear out the states.

  • BRIAN YU: Clear out the state's.

  • Clear out the input field so that we say this .setstate.

  • Front is going to be empty.

  • Back is going to be empty.

  • And so we're saying clear out those input

  • fields so that we can start fresh.

  • Go ahead and refresh the page.

  • Front of the card, it will be test three front.

  • Back of the card, test three back.

  • I'll go ahead and click Add card.

  • And all right, great, we got a new card in this list of cards.

  • And the input fields cleared themselves out.

  • At this point, I'm going to go back and delete these sample cards

  • that we had just for testing purposes.

  • We'll go back to just starting with an empty list.

  • And now I can begin to add whatever flashcards I want.

  • I can say, OK, 1 plus 1, plus 2.

  • We can do as many of these as you want to.

  • But of course, the Delete button doesn't yet work.

  • We can create all the cards, but we can't yet delete a card.

  • And so to do that, it's going to be basically

  • the exact same thing as what we just did for adding

  • a card, but the analog for deleting, as we did with the tasks example.

  • So inside of the app, we had a function called Add

  • card, which took care of adding a card.

  • But our app is also going to need to know how to delete a card.

  • And to delete a card, we're going to delete a card based on its index.

  • And this code should look very familiar to the code we

  • did when we were doing task management.

  • We're going to say, all right, this .setstate.

  • It's going to take the original state.

  • And let's go ahead and say, all right, the cards,

  • let's make a copy of state.cards.

  • And let's go ahead and splice out of the cards

  • remove something from the cards array at index.

  • Remove one thing and go ahead and return cards

  • is equal to whatever the value of cards is.

  • So what's happening here?

  • When I delete a card, at this particular index, I'm saying,

  • OK, make a copy of state.cards, store it in this variable called cards,

  • remove what was ever at this index, remove

  • one element using the splice method.

  • And then I'm returning the new state, update the value of cards in the state

  • to be equal to this variable cards.

  • And as we've seen a couple of times now, because these things have

  • the same name, you could even simplify this to just return cards.

  • And we'll implicitly assume that they have the same name.

  • So now I have this delete card method in my app

  • that operates in order to delete a card.

  • This is also a function that card Editor needs to have access to.

  • So we'll say delete card equals this .deletecard.

  • And we'll go ahead and now go to the card Editor.

  • And we'll do the same thing we did before, this button will

  • give a data index property of--

  • I'm sorry, not the Add card button.

  • The Delete button-- yeah, here's the Delete button--

  • will give it a data/index property of, all right,

  • what is the index of which element to delete if I try and delete this card?

  • It'll be I for example.

  • And then when I click on it, we're going to call the this .deletecard method.

  • Same as the example from earlier this morning.

  • And now when I delete a card, that's going

  • to be a function that takes the event as input.

  • And why do we need the event?

  • Well, because event.target.dataset.index.

  • In other words, take the click event, get out

  • its target, the button that I clicked on, get at its data

  • attributes, and specifically, get at the data-index attribute of the button

  • then I clicked on.

  • This is the index of the card I want to delete.

  • And so to actually do the deletion, I'll say this .props.deletecard.

  • I'm deleting whatever was at that index.

  • So I have a delete card function inside of the card Editor

  • that calls the delete card function that was provided to me as one of the props.

  • And the only difference is this function, delete card,

  • is going to take care of providing to the apps delete card

  • the index that I actually want to delete.

  • Yeah?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: Repeat that?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: We don't need to wrap it because we're not actually calling this

  • .deletecard.

  • I'm not saying this .deletecardI, which you could do.

  • I'm just saying this .deletecard.

  • And so that is a function that we'll run when the button is clicked.

  • And so there's no need to wrap that inside of an outer function.

  • Yeah?

  • AUDIENCE: Why can you find delete card twice separately

  • rather than just call the card function that's

  • defined in the app and [INAUDIBLE]

  • BRIAN YU: So you could do it that way.

  • You could wrap this in a function and say, this .props.deletecardI.

  • And that would work just fine.

  • Basically, having a function that when you call it

  • is going to go to props.deletecard and delete that element.

  • But I'm putting it separately for the efficiency reason

  • that I described before of not regenerating these functions over

  • and over.

  • Let's check to make sure this actually works.

  • So let's do test, test.

  • Another one, another one.

  • If I delete a card, all right, it gets removed.

  • If I I delete another card, we're able to add and delete cards

  • from the card Editor.

  • Questions about anything?

  • Yeah?

  • AUDIENCE: [INAUDIBLE].

  • BRIAN YU: Good question.

  • So the delete card function inside of the app class

  • is a delete card function that just takes an index and removes

  • whatever card is at that index.

  • And I had another function in card Editor

  • that I could have given a different name.

  • But it's deleting a card, so I decided to give it the same name.

  • And all this function is doing is it is calling the original function,

  • passing in which card to actually delete.

  • You could conceive of other models where you consolidate these

  • into one function.

  • I separated into two for the sake of separating

  • the behavior of deleting a card at a particular index

  • and responding to the fact that you clicked on a button that is supposed

  • to do the deleting of the card.

  • Though if you'd like to practice, it would be an interesting exercise

  • to consolidate this to just a single function.

  • And you can try doing it that way.

  • All right, so now we have the ability to edit cards.

  • And all that remains is this card Viewer.

  • And so we have this, this is the Viewer page where in theory you'd

  • be able to view whatever contents of the cards that you have,

  • because card Viewer is going to provide this cards argument that

  • is going to contain all of the cards in the current state.

  • Questions?

  • OK, so there's a lot of code here, a lot of interacting and moving pieces.

  • We have an app that is this big component that

  • contains within it two smaller components, a card Editor and a card

  • Viewer.

  • And there's a lot of interaction between these components.

  • The app is providing props to the card Editor,

  • providing information about the cards, providing event handlers

  • for what to do in order to add a card or remove a card.

  • And in response, the card Editor is calling a lot of those functions

  • that it was provided in order to say, OK, update the application state,

  • update the cards or change the mode from the Editor to the Viewer,

  • or vise versa.

  • So I definitely encourage you to take a look at this example--

  • I'll post it online in just a moment--

  • and try and tease it apart.

  • Try and figure out how it's working.

  • In fact, potentially try and add new features to it if you'd like.

  • And the main project of this afternoon is going to be to extend on this.

  • The first step is going to be understand the code that exists there.

  • But then let's think about trying to add some more possibilities.

  • So adding flash cards is a feature that we already have.

  • Displaying the flash cards in a table, the second thing

  • here is a feature that we already have.

  • And it's these last two bits that are worth trying to implement here.

  • Try and implement the card Viewer, some way of viewing the flash cards.

  • And at first, just see if you can get one flash

  • card to display the front of card number one, for example.

  • And then see if you can add some notion of,

  • all right, when you click on something, have an on-click handler

  • that flips the card over.

  • In other words, goes from storing the front to the back.

  • And you might think about the kind of state that you want to store that.

  • You probably want something inside your application state that's

  • keeping track of are you on the front of the card

  • or are you on the back of the card?

  • And when you click on the card, you just need

  • to flip that state around going from front to back.

  • In much the same way that we went from Editor to Viewer,

  • when you clicked on a button, you can go from front to back of the card

  • by clicking on the card.

  • You can display one card, and show the card, and flip it over to the back.

  • Consider a button that moves you on to the next flash cards.

  • So you're on card number one, you press a button,

  • it takes you to card number two.

  • And that's very similar in spirit to just this idea

  • of thinking about what you need to store inside of your application state.

  • In addition to having information about are you on the front of the card

  • or the back of the card, you probably also want some information

  • about which card are you currently on?

  • Some notion inside the state of I am currently

  • on card zero, or card one, or card two, whereby you can change that state value

  • in order to show a different card.

  • So go ahead and give that a shot.

  • If you manage to finish that, there are certainly

  • other features you can consider.

  • I listed a couple here.

  • Consider a button that will shuffle the cards and put them in a random order,

  • for instance.

  • Or marking cards as learned or not, the one you might see on a quiz app

  • like Quizlet, for example.

  • And so plenty of room to explore here as well.

  • Feel free to continue working on the to-do list application

  • if you'd like to.

  • The staff will be around to certainly help you up until 5:00.

  • And tomorrow, we're going to spend even more time on React.

  • So if this all seems new and unfamiliar to you,

  • know that tomorrow we'll be spending more time on the exact same topic

  • so that we can help you become familiar with it,

  • help you get used to the different way of thinking with regards to React.

  • And so all that and more with CS50 Beyond.

  • So we'll go ahead and turn to our afternoon project now.

  • Feel free to stick around here in the auditorium.

  • And come and find the staff if you need help with anything.

[MUSIC PLAYING]

Subtitles and vocabulary

Click the word to look it up Click the word to find further inforamtion about it