4.2 Facilitating Child-to-Parent Communication
Data flows from parent components to their children. In order for children to communicate with their parents, we have to use custom events. You'll learn how in this lesson.
1.Introduction2 lessons, 06:11
2.Getting Started2 lessons, 14:39
3.Building the Hangman Game5 lessons, 39:23
4.Working With Data4 lessons, 32:39
5.Conclusion1 lesson, 00:56
4.2 Facilitating Child-to-Parent Communication
In the previous lesson, we started implementing the functionality for our buttons, so that whenever we click on them, they are tracking their own state and they hide themselves. But they need to do more than just that. They need to notify the application that the user clicked on a button that represents the letter so that the application can do something with that letter. So then the question becomes how do we do that? Because if you've noticed, data tends to flow from parents to child. For example, inside of app, this is where we are using the game board component, and inside of app the secret word is originated. This value TutsPlus originates here inside of the app component and it's being passed down to the game board through the secret word, prop. Well, if we look at the game board, we see the same pattern to where the secret word is then flowing down into one of its children, the LetterGrid using the secret word prop. So the flow of data goes from parent to child, and there's not a built in mechanism that allows the opposite for a child to pass data to the parent. There are however programming patterns that we can use, very common programming patterns that we can use to facilitate the child to parent communication. And we have already done so. We did so in the previous lesson within our own button component. Because remember that what looks like HTML is still just a React component. So this HTML button has a prop called onClick. And then whatever data the HTML button needs to supply to our button component, it will pass that as an argument, which in this particular case is just any event object. So we can take that same methodology and apply it to our own custom components. Like for example, we can add an onClick event handler for our button component because all this onClick is as far as a React component is concerned is a prop. So we can go to the ButtonGrid. And we can say that we want to handle this onClick event and we would do so using a clickHandler. So of course, we would have a function called clickHandler that would then execute when this button is clicked. And the way that we would wire that up inside of our button component is like this. It's a prop, so we are going to, first of all, destructure that from the props so that we have easy access to it. And then inside of this clickHandler, we are going to call onClick, and then we will pass in the value, the letter that this button is supposed to represent. So this means that whatever is handling our onClick event will then have that letter to work with. But then the problem is that the ButtonGrid itself doesn't necessarily need to know what letter was clicked. Because as far as the functionality of this component is concerned, we are essentially done. What does need to know, is the game board, because it needs to modify this array called guessedLetters so that the LetterGrid could handle that. So what this means is that our ButtonGrid could have its own kind of event like guessedLetters or let's call it letterGuessed. And we would, of course, set this to a function, but for right now this ButtonGrid has a prop called letterGuessed. So we could define that in our props here. We will destructure that, and then we could pass that on to our button component as the handler for the click event. So in this case, whenever we click on our button, it's going to trigger this onClick, which is going to call whatever was passed to the ButtonGrid from the game board. And so let's define a function. We'll just call it letterGuessedHandler. So let's define this function. We're going to have the letter to work with, but then we need to add it to this array that's being passed to the guesedLetters for the LetterGrid. Well, this would be another opportunity to useState. This array that we are using needs to be updated every time a letter is guessed. So this game board needs to track its own state to do that. So let's add the import statement for useState. And then we will create some stateful variables that we can use to track this information. Like for example, we can call this guessedLetters. And then we will have the function that we would use to update this value, so we would call that setGuessedLetters. And since this is an array, we will initialize it as an empty array whenever we call useState. And then inside of our letterGuessedHandler, the first thing we want to do is take the letter and convert it to lowercase. Because, remember, we want to normalize the string so that we don't have to worry about uppercase and lowercase characters. So we will have our lowercase character and then we will add that to our guessedLetters, that is our state. So we would call setGuessedLetters, and then we would supply the new value for the array that contains our guessed letters. But the issue here is array is a very special type of value, it's more than just a string or a Boolean. So, what we need to do is pass a function to setGuessedLetters, which is going to execute, and this function will then update the value that will then be set to guessedLetters. And this is gonna look a little funky, but this is how we do it. We're going to create a new array, and we're going to spread out the current array, which is represented by this previous value as the parameter. Then we will include as the next element in the array, the lowercase letter that was given to us, and that is going to update the guessedLetters with a new value. You might think that we could do this a lot easier by just saying guessedLetters, we could push in that new value, and then we could call setGuessedLetters and pass in guessedLetters,. Unfortunately no, we can't do that. That's not going to tell React that the value has changed which will then cause React to redraw the appropriate components. This is the only way that we can get around this. So let's very briefly go over this once again. We have wired up our own custom click event for our button component that will be called when the user actually clicks on the underlying button. That click event listener is handled by whatever function is being used as the letterGuessed event on our ButtonGrid. And that function is being defined inside of our game board. So when the function executes, it's going to get the letter that was clicked, but then we need to update the state so that React will know to redraw everything that it needs to redraw. So we call the setGuessedLetters function that was given to us whenever we created our state. We are passing a function that works with the previous value of guessedLetters, and we are setting it to a new value of a new array that contains all of the previous elements plus the new element. And so then the only thing we need to do is use guessedLetters right here whenever we pass it down to the LetterGrid. So if we go back to the browser, we are going to see our application, it's almost completely functional. We can guess whatever letters that we want. If they appear within the secret word, then they automatically appear. If not, the button hides itself so that it cannot be guessed again. Now I understand that this can be a little confusing, and I encourage you to rewatch this lesson until you understand what's going on, because this is a very common pattern that we use in our React components. It is the only way that a child can communicate with a parent. Setting up a custom event is the only way that a child can communicate with the parent. So we essentially created two custom events. We created one called onClick for our button component. We created a second one on the ButtonGrid called letterGuessed. Once it clicks, it's really quite simple because all these are props and we are supplying a function as a value for those props. So with that addition, we are essentially done with our application, at least, as far as the basic Hangman functionality is concerned. There are of course some other things that we can add to give the user more feedback, which is something that we will start doing in the next lesson.