[docs] Use controlled components for tutorial
Tay Yang Shun committed
Nov 18, 2015 at 14:43 UTC
3812b95450fac28fb7c17d9cc57ba2edf9170aac
1 file changed
+103
-29
docs/docs/tutorial.md
+103
-29
@@ -492,28 +492,91 @@ var CommentForm = React.createClass({
492
});
493
```
494
495
-Let's make the form interactive. When the user submits the form, we should clear it, submit a request to the server, and refresh the list of comments. To start, let's listen for the form's submit event and clear it.
495
+#### Controlled components
496
+
497
+With the traditional DOM, `input` elements are rendered and the browser manages the state (its rendered value). As a result, the state of the actual DOM will differ from that of the component. This is not ideal as the state of the view will differ from that of the component. In React, components should always represent the state of the view and not only at the point of initialization.
498
+
499
+Hence, we will be using `this.state` to save the user's input as it is entered. We define an initial `state` with two properties `author` and `text` and set them to be empty strings. In our `<input>` elements, we set the `value` prop to reflect the `state` of the component and attach `onChange` handlers to them. These `<input>` elements with a `value` set are called controlled components. Read more about controlled components on the [Forms article](/react/docs/forms.html#controlled-components).
500
497
-```javascript{3-14,16-19}
501
+```javascript{3-11,15-26}
502
// tutorial16.js
503
var CommentForm = React.createClass({
504
+ getInitialState: function() {
505
+ return {author: '', text: ''};
506
+ },
507
+ handleAuthorChange: function(e) {
508
+ this.setState({author: e.target.value});
509
+ },
510
+ handleTextChange: function(e) {
511
+ this.setState({text: e.target.value});
512
+ },
513
+ render: function() {
514
+ return (
515
+ <form className="commentForm">
516
+ <input
517
+ type="text"
518
+ placeholder="Your name"
519
+ value={this.state.author}
520
+ onChange={this.handleAuthorChange}
521
+ />
522
+ <input
523
+ type="text"
524
+ placeholder="Say something..."
525
+ value={this.state.text}
526
+ onChange={this.handleTextChange}
527
+ />
528
+ <input type="submit" value="Post" />
529
+ </form>
530
+ );
531
+ }
532
+});
533
+```
534
+
535
+#### Events
536
+
537
+React attaches event handlers to components using a camelCase naming convention. We attach `onChange` handlers to the two `<input>` elements. Now, as the user enters text into the `<input>` fields, the attached `onChange` callbacks are fired and the `state` of the component is modified. Subsequently, the rendered value of the `input` element will be updated to reflect the current component `state`.
538
+
539
+#### Submitting the form
540
+
541
+Let's make the form interactive. When the user submits the form, we should clear it, submit a request to the server, and refresh the list of comments. To start, let's listen for the form's submit event and clear it.
542
+
543
+```javascript{12-21,24}
544
+// tutorial17.js
545
+var CommentForm = React.createClass({
546
+ getInitialState: function() {
547
+ return {author: '', text: ''};
548
+ },
549
+ handleAuthorChange: function(e) {
550
+ this.setState({author: e.target.value});
551
+ },
552
+ handleTextChange: function(e) {
553
+ this.setState({text: e.target.value});
554
+ },
555
handleSubmit: function(e) {
556
e.preventDefault();
502
- var author = this.refs.author.value.trim();
503
- var text = this.refs.text.value.trim();
557
+ var author = this.state.author.trim();
558
+ var text = this.state.text.trim();
559
if (!text || !author) {
560
return;
561
}
562
// TODO: send request to the server
508
- this.refs.author.value = '';
509
- this.refs.text.value = '';
510
- return;
563
+ this.setState({author: '', text: ''});
564
},
565
render: function() {
566
return (
567
<form className="commentForm" onSubmit={this.handleSubmit}>
515
- <input type="text" placeholder="Your name" ref="author" />
516
- <input type="text" placeholder="Say something..." ref="text" />
568
+ <input
569
+ type="text"
570
+ placeholder="Your name"
571
+ value={this.state.author}
572
+ onChange={this.handleAuthorChange}
573
+ />
574
+ <input
575
+ type="text"
576
+ placeholder="Say something..."
577
+ value={this.state.text}
578
+ onChange={this.handleTextChange}
579
+ />
580
<input type="submit" value="Post" />
581
</form>
582
);
@@ -521,24 +584,18 @@ var CommentForm = React.createClass({
584
});
585
```
586
524
-##### Events
525
-
526
-React attaches event handlers to components using a camelCase naming convention. We attach an `onSubmit` handler to the form that clears the form fields when the form is submitted with valid input.
587
+We attach an `onSubmit` handler to the form that clears the form fields when the form is submitted with valid input.
588
589
Call `preventDefault()` on the event to prevent the browser's default action of submitting the form.
590
530
-##### Refs
531
-
532
-We use the `ref` attribute to assign a name to a child component and `this.refs` to reference the DOM node.
533
-
534
-##### Callbacks as props
591
+#### Callbacks as props
592
593
When a user submits a comment, we will need to refresh the list of comments to include the new one. It makes sense to do all of this logic in `CommentBox` since `CommentBox` owns the state that represents the list of comments.
594
595
We need to pass data from the child component back up to its parent. We do this in our parent's `render` method by passing a new callback (`handleCommentSubmit`) into the child, binding it to the child's `onCommentSubmit` event. Whenever the event is triggered, the callback will be invoked:
596
597
```javascript{16-18,31}
541
-// tutorial17.js
598
+// tutorial18.js
599
var CommentBox = React.createClass({
600
loadCommentsFromServer: function() {
601
$.ajax({
@@ -577,26 +634,43 @@ var CommentBox = React.createClass({
634
635
Let's call the callback from the `CommentForm` when the user submits the form:
636
580
-```javascript{10}
581
-// tutorial18.js
637
+```javascript{19}
638
+// tutorial19.js
639
var CommentForm = React.createClass({
640
+ getInitialState: function() {
641
+ return {author: '', text: ''};
642
+ },
643
+ handleAuthorChange: function(e) {
644
+ this.setState({author: e.target.value});
645
+ },
646
+ handleTextChange: function(e) {
647
+ this.setState({text: e.target.value});
648
+ },
649
handleSubmit: function(e) {
650
e.preventDefault();
585
- var author = this.refs.author.value.trim();
586
- var text = this.refs.text.value.trim();
651
+ var author = this.state.author.trim();
652
+ var text = this.state.text.trim();
653
if (!text || !author) {
654
return;
655
}
656
this.props.onCommentSubmit({author: author, text: text});
591
- this.refs.author.value = '';
592
- this.refs.text.value = '';
593
- return;
657
+ this.setState({author: '', text: ''});
658
},
659
render: function() {
660
return (
661
<form className="commentForm" onSubmit={this.handleSubmit}>
598
- <input type="text" placeholder="Your name" ref="author" />
599
- <input type="text" placeholder="Say something..." ref="text" />
662
+ <input
663
+ type="text"
664
+ placeholder="Your name"
665
+ value={this.state.author}
666
+ onChange={this.handleAuthorChange}
667
+ />
668
+ <input
669
+ type="text"
670
+ placeholder="Say something..."
671
+ value={this.state.text}
672
+ onChange={this.handleTextChange}
673
+ />
674
<input type="submit" value="Post" />
675
</form>
676
);
@@ -607,7 +681,7 @@ var CommentForm = React.createClass({
681
Now that the callbacks are in place, all we have to do is submit to the server and refresh the list:
682
683
```javascript{17-28}
610
-// tutorial19.js
684
+// tutorial20.js
685
var CommentBox = React.createClass({
686
loadCommentsFromServer: function() {
687
$.ajax({
@@ -660,7 +734,7 @@ var CommentBox = React.createClass({
734
Our application is now feature complete but it feels slow to have to wait for the request to complete before your comment appears in the list. We can optimistically add this comment to the list to make the app feel faster.
735
736
```javascript{17-23}
663
-// tutorial20.js
737
+// tutorial21.js
738
var CommentBox = React.createClass({
739
loadCommentsFromServer: function() {
740
$.ajax({