In a previous tutorial, we built a reusable React dialog box where we could provide a title and a body that could change depending upon where we're using the dialog box. In this tutorial, I'd like to show you how we can pass a component in as a property to customize the Save button and also pass along an event handler from a parent component down to a child.
Here's our dialog box component from the previous tutorial. If you missed it, check the show notes. I'll post a link so that you can go back and watch.
The first thing I would like to do is to clean this up a little bit by providing two new instance methods to the component, one for open and the other for close. And instead of calling this .setState, I'm going to put that inside of the close method, and then I'll copy it and put isShown:true inside of the open method. That way we can call the open and close method from the parent component. Inside the handleClose event handler now, instead of this what I had previously, I will write this.close.
Now, down at the bottom where we have the HTML, notice that in the modal footer we have a Close button where the handleClose event handler will close this dialog box. But then we have this Save changes button that currently doesn't do anything. I've moved the properties into separate lines so that it's easier to read.
The first thing I want to do here is to customize the HTML that goes inside of this button. So to do that, I'm going to grab it from the properties that get passed into the component instance. I could call this the saveButtonHtml, or whatever property name you would like.
Now let's go to the Home component where this dialog box is actually used. Here's where we're passing properties to the dialog box, which will be available under this dot props inside of the instance of the dialog. So one of the cool things about JSX is this property value can also be another component. We can use tags like this directly in the property values.
So let's create a property for the HTML body, or the HTML text for the button. I'll use the let keyword, and I'll call this the buttonHtml and just write regular JSX tags here. So I'll create a span element, and we'll give it a class name of save button, and I'll put the text Save Changes in here. And then we can pass this along as a variable directly to the dialog box like so. And let's change this to saveButtonHtml to make it more clear.
Inside the browser, you can see the new HTML for the button shows up right on the page. And if you click down into the React half of the debugger and expand, you can see the save button has made its way down into the footer. So being able to pass JSX tags or HTML tags like this as variables allows us to compose components out of other components, and it's a really flexible, powerful feature of React.
Next, when I click Save Changes, nothing happens currently. So let's go add an event handler to the Home component that will respond to this click event. Inside the dialog box component file, scroll down to the bottom of the page, and let's add a click handler to the Save button.
I'll define a method in a second called handle save button click that is going to respond to this click event. So if we scroll up a bit to the top here, I can create that event handler handleSaveButtonClick, and it will take an event object as a parameter. And for now, let's just close the dialog and make sure that it's still working.
When I click Save Changes, it looks like it's working correctly. And if I go to the React Debug tab and click on the button, I can even see the onClick event handler in the debug under the Properties area.
In the DialogBox component, when we click the Save button, instead of just closing the dialog, what I'd like to do is to give the user of this component a way to perform some logic or do something before the dialog closes. We can let the user of the component provide a property that will get passed into this.props, and that property could be a method that we call when the user clicks the Save button.
So I'll call the onSave method that's provided, and then I'll use the spread operator to pass along the arguments from this function. We used to do onSave.apply (this, arguments), but we don't need to do that anymore with ES6. We can just use the spread operator.
What I can even do is assign the result of that to a variable and then check whether or not the function returns false. And if it does, we can keep the dialog box open. So here I'll check whether or not the result-- if it does not equal false, then we'll close the dialog. And this is the strict false checker, or equality checker, to see whether or not it's actually false, not null or undefined. So if the onSave function returns false-- or does not return false, then we'll close the dialog box.
Now, so far, we haven't checked to see whether or not onSave is even defined. So what we should really do here is enforce the fact that the onSave property is required in order to use this dialog box. We can do that by adding another property to the create class object called propTypes. The value will be an object, and that object will have the property names as keys.
So in this example, I'll use onSave, and then the value will be the type of value that we want that property to be. Those different types are defined on the React.propTypes namespace. And you can see that the value here is supposed to be a function so that we're telling React that it should be a function. And we're also telling React that it's required. So we'll get a little warning if we don't pass the onSave property to this component.
Let's check that out in the browser to make sure. Ignore the second warning for now, but look at the top one. It's a warning, not an error. And it's saying that the "Required property onSave was not specified in the dialog box." So back in our Home component, let's provide a new property here called onSave, and we'll set that equal to a method that we'll create in a second called handleSave.
And handleSave will be a method that takes an event object as a parameter. And for now, all it's going to do is log to the console that we've been saved. And then it will return true anything that's not false, strictly false, so that we close the dialog.
And while we're up here, instead of calling setState directly, we fix that so that we can just call the open method. A little bit better of an API design. Let's make sure this works. Now when I click the Save button, you can see the save text is printed to the console as expected.
React properties are really powerful. And in this tutorial, I showed you how we can take some JSX tags that creates a React element, assign that to a variable, and pass it in as a value to a property to another component. We also saw how you can pass a function in as a value that can get called at a later time by the dialog box component.