As we've seen, the state
and prop
will give you the control to alter the value of the component and handle the state for that component.
OK, now let's add some advanced features in our Add Ticket form, which can validate the user input and display the tickets on the UI.
React provides ref
non-DOM attributes to access the component. The ref
attribute can be a callback function and it will execute immediately after the component is mounted.
So we will attach the ref
attribute in our form element to fetch the values:
var AddTicket = React.createClass({ handleSubmitEvent: function (event) { event.preventDefault(); console.log("Email--"+this.refs.email.value.trim()); console.log("Issue Type--"+this.refs.issueType.value.trim()); console.log("Department--"+this.refs.department.value.trim()); console.log("Comments--"+this.refs.comment.value.trim()); }, render: function() { return ( ); } });
Now, we'll add the JSX of form elements inside the return
method:
<form onSubmit={this.handleSubmitEvent}> <div className="form-group"> <label htmlFor="email">Email <span style={style}>*</span> </label> <input type="text" id="email" className="form-control" placeholder="Enter email" required ref="email"/> </div> <div className="form-group"> <label htmlFor="issueType">Issue Type <span style={style}>* </span></label> <select className="form-control" id="issueType" required ref="issueType"> <option value="">-----Select----</option> <option value="Access Related Issue">Access Related Issue</option> <option value="Email Related Issues">Email Related Issues</option> <option value="Hardware Request">Hardware Request</option> <option value="Health & Safety">Health & Safety</option> <option value="Network">Network</option> <option value="Intranet">Intranet</option> <option value="Other">Other</option> </select> </div> <div className="form-group"> <label htmlFor="department">Assign Department <span style= {style}>*</span></label> <select className="form-control" id="department" required ref="department"> <option value="">-----Select----</option> <option value="Admin">Admin</option> <option value="HR">HR</option> <option value="IT">IT</option> <option value="Development">Development</option> </select> </div> <div className="form-group"> <label htmlFor="comments">Comments <span style={style}>*</span> </label>(<span id="maxlength">200</span> characters left) <textarea className="form-control" rows="3" id="comments" required ref="comment"></textarea> </div> <div className="btn-group"> <button type="submit" className="btn btn-primary">Submit</button> <button type="reset" className="btn btn-link">cancel</button> </div> </form>
In the preceding code, I have added the ref
attribute on our form elements and onSubmit
, calling the function name, handleSubmitEvent
. Inside this function, we are fetching the values with this.refs
.
Now, open your browser and let's see the output of our code:
We are successfully getting the values for our component. It's very clear how data is flowing in our component. In the console we can see the values of the form when the user clicks the Submit button.
Now, let display this ticket info in our UI.
First, we need to get the value of the form and manage the state of the form:
var AddTicket = React.createClass({ handleSubmitEvent: function (event) { event.preventDefault(); var values = { date: new Date(), email: this.refs.email.value.trim(), issueType: this.refs.issueType.value.trim(), department: this.refs.department.value.trim(), comment: this.refs.comment.value.trim() }; this.props.addTicketList(values); }, )};
Now we will create the AddTicketsForm component, which will be responsible for managing and holding the state of addTicketList(values):
var AddTicketsForm = React.createClass({ getInitialState: function () { return { list: {} }; }, updateList: function (newList) { this.setState({ list: newList }); }, addTicketList: function (item) { var list = this.state.list; list[item] = item; //pass the item.id in array if we are using key attribute. this.updateList(list); }, render: function () { var items = this.state.list; return ( <div className="container"> <div className="row"> <div className="col-sm-6"> <List items={items} /> <AddTicket addTicketList={this.addTicketList} /> </div> </div> </div> ); } });
Let's take a look at the preceding code:
getInitialState
: This initializes the default state for the
<List />
componentaddTicketList
: This holds the value and passes into the updateList
with the stateupdateList
: This is for updating the list of the tickets to make our UI in syncNow we need to create the <List items={items} />
component, which iterates the list when we submit the form:
var List = React.createClass({ getListOfIds: function (items) { return Object.keys(items); }, createListElements: function (items) { var item; return ( this .getListOfIds(items) .map(function createListItemElement(itemId) { item = items[itemId]; return (<ListPanel item={item} />);//key={item.id} }.bind(this)) .reverse() ); }, render: function () { var items = this.props.items; var listItemElements = this.createListElements(items); return ( <div className="bg-info"> {listItemElements} </div> ); } });
Let's get an understanding of the preceding code:
getListOfIds
: This will iterate through all the keys in the item, and it will return the list that we have mapped with the <ListPanel item={item}/>
component.bind(this)
: The this
keyword will be passed as a second argument, which gives the appropriate value when the function is calledIn the render
method, we are just rendering the list of elements. In addition, we can also add a condition based on the length inside the render
method:
<p className={listItemElements.length > 0 ? "":"bg-info"}> {listItemElements.length > 0 ? listItemElements : "You have not raised any ticket yet. Fill this form to submit the ticket"} </p>
It will validate the length, and based on the return value TRUE or FALSE, it will display the message or apply the Bootstrap class, .bg-info
.
Now we need to create a <ListPanel />
component that displays the list of tickets in the UI:
var ListPanel = React.createClass({ render: function () { var item = this.props.item; return ( <div className="panel panel-default"> <div className="panel-body"> {item.issueType}<br/> {item.email}<br/> {item.comment} </div> <div className="panel-footer"> {item.date.toString()} </div> </div> ); } });
Now, let's combine our code and see the result in the browser:
var style = {color: "#ffaaaa"}; var AddTicketsForm = React.createClass({ getInitialState: function () { return { list: {} }; }, updateList: function (newList) { this.setState({ list: newList }); }, addTicketList: function (item) { var list = this.state.list; list[item] = item; this.updateList(list); }, render: function () { var items = this.state.list; return ( <div className="container"> <div className="row"> <div className="col-sm-12"> <List items={items} /> <AddTicket addTicketList={this.addTicketList} /> </div> </div> </div> ); } }); //AddTicketsForm components code ends here var ListPanel = React.createClass({ render: function () { var item = this.props.item; return ( <div className="panel panel-default"> <div className="panel-body"> {item.issueType}<br/> {item.email}<br/> {item.comment} </div> <div className="panel-footer"> {item.date.toString()} </div> </div> ); } }); // We'll wrap ListPanel component in List var List = React.createClass({ getListOfIds: function (items) { return Object.keys(items); }, createListElements: function (items) { var item; return ( this .getListOfIds(items) .map(function createListItemElement(itemId) { item = items[itemId]; return ( <ListPanel item={item} /> );//key={item.id} }.bind(this)) .reverse() ); }, render: function () { var items = this.props.items; var listItemElements = this.createListElements(items); return ( <p className={listItemElements.length > 0 ? "":"bg-info"}> {listItemElements.length > 0 ? listItemElements : "You have not raised any ticket yet. Fill this form to submit the ticket"} </p> ); } });
In the preceding code, we are iterating the items and passing as a props in <Listpanel/> component:
var AddTicket = React.createClass({ handleSubmitEvent: function (event) { event.preventDefault(); var values = { date: new Date(), email: this.refs.email.value.trim(), issueType: this.refs.issueType.value.trim(), department: this.refs.department.value.trim(), comment: this.refs.comment.value.trim() }; this.props.addTicketList(values); }, render: function() { return ( // Form template ReactDOM.render( <AddTicketsForm />, document.getElementById('form') );
Here is the markup of our HTML page:
<link rel="stylesheet" href="css/bootstrap.min.css"> <style type="text/css"> div.bg-info { padding: 15px; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-sm-6"> <h2>Add Ticket</h2> <hr/> </div> </div> </div> <div id="form"> </div> <script type="text/javascript" src="js/react.js"></script> <script type="text/javascript" src="js/react-dom.js"></script> <script src="js/browser.min.js"></script> <script src="component/advance-form.js" type="text/babel"></script> </body>
Open your browser and let's see the output of our form before submitting:
The following screenshot shows how it looks after submitting the form:
This looks good. Our first fully functional React component is ready.
Observe the following screenshot:
We are getting this warning message because of the key
(an optional) attribute of React, which accepts a unique ID. Every time when we submit the form, it will iterate the List
component to update the UI. For example:
createListElements: function (items) { var item; return ( this .getListOfIds(items) .map(function createListItemElement(itemId,id) { item = items[itemId]; return (<ListPanel key={id} item={item} />); }.bind(this)) .reverse() ); },
React provides the add-ons module to solve this type of warning and generate the unique ID, but it is only available in npm. In further chapters, we will show how we can work with React npm modules. Here is a list of some popular add-ons:
TransitionGroup
and CSSTransitionGroup
: For dealing with animations and transitionsLinkedStateMixin
: To make easy interaction with the user's form input data and the component's statecloneWithProps
: Changes the props of the component and makes shallow copiescreateFragment
: Used to create a set of externally keyed childrenUpdate
: A helper function that makes it easy to deal with data in JavaScriptPureRenderMixin:
A performance boostershallowCompare:
A helper function to do shallow comparison for props and stateBootstrap provides some helper classes to give you a better user experience. In the AddTicketsForm
form component, we have used the Bootstrap helper classes *-info
, which helps you to convey the meaning of your message with color for screen readers. Some of these are *-muted
, *-primary
, *-success
, *-info
, *-warning
, and *-danger
.
To change the color of the text we can use .text*
:
<p class="text-info">...</p>
To change the background color we can use .bg*
:
<p class="bg-info">...</p>
To display the caret that will indicate the direction of the dropdown, we can use:
<span class="caret"></span>