├── requirements.txt
├── .gitignore
├── .editorconfig
├── app.json
├── LICENSE
├── comments.json
├── package.json
├── public
├── index.html
├── css
│ └── base.css
└── scripts
│ └── example.js
├── README.md
├── server.pl
├── server.py
├── server.rb
├── server.php
├── server.js
└── server.go
/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask==0.10.1
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | node_modules
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 4
8 | indent_style = space
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.{js,rb,css,html}]
13 | indent_size = 2
14 |
15 | [*.go]
16 | indent_size = 8
17 | indent_style = tab
18 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "React Tutorial Server",
3 | "description": "Code from the React tutorial",
4 | "keywords": [ "react", "reactjs", "tutorial" ],
5 | "repository": "https://github.com/reactjs/react-tutorial",
6 | "logo": "https://facebook.github.io/react/img/logo.svg",
7 | "website": "http://facebook.github.io/react/docs/tutorial.html",
8 | "success_url": "/",
9 | "env" : {
10 | "BUILDPACK_URL": "https://github.com/heroku/heroku-buildpack-nodejs.git"
11 | }
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The examples provided by Facebook are for non-commercial testing and evaluation
2 | purposes only. Facebook reserves all rights not expressly granted.
3 |
4 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
7 | FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
8 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
9 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/comments.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1388534400000,
4 | "author": "Pete Hunt",
5 | "text": "Hey there!"
6 | },
7 | {
8 | "id": 1420070400000,
9 | "author": "Paul O’Shannessy",
10 | "text": "React is *great*!"
11 | },
12 | {
13 | "id": 1464988635157,
14 | "author": "ben",
15 | "text": "*abc*"
16 | },
17 | {
18 | "id": 1464988636500,
19 | "author": "ben",
20 | "text": "*abc*"
21 | },
22 | {
23 | "id": 1464988717637,
24 | "author": "evil",
25 | "text": "alert(1)"
26 | }
27 | ]
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-tutorial",
3 | "version": "0.0.0",
4 | "private": true,
5 | "license": "see LICENSE file",
6 | "description": "Code from the React tutorial.",
7 | "main": "server.js",
8 | "dependencies": {
9 | "body-parser": "^1.4.3",
10 | "express": "^4.4.5"
11 | },
12 | "devDependencies": {},
13 | "scripts": {
14 | "test": "echo \"Error: no test specified\" && exit 1",
15 | "start": "node server.js"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/reactjs/react-tutorial.git"
20 | },
21 | "keywords": [
22 | "react",
23 | "tutorial",
24 | "comment",
25 | "example"
26 | ],
27 | "author": "petehunt",
28 | "bugs": {
29 | "url": "https://github.com/reactjs/react-tutorial/issues"
30 | },
31 | "homepage": "https://github.com/reactjs/react-tutorial",
32 | "engines" : {
33 | "node" : "0.12.x"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React Tutorial
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/css/base.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #fff;
3 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
4 | font-size: 15px;
5 | line-height: 1.7;
6 | margin: 0;
7 | padding: 30px;
8 | }
9 |
10 | a {
11 | color: #4183c4;
12 | text-decoration: none;
13 | }
14 |
15 | a:hover {
16 | text-decoration: underline;
17 | }
18 |
19 | code {
20 | background-color: #f8f8f8;
21 | border: 1px solid #ddd;
22 | border-radius: 3px;
23 | font-family: "Bitstream Vera Sans Mono", Consolas, Courier, monospace;
24 | font-size: 12px;
25 | margin: 0 2px;
26 | padding: 0 5px;
27 | }
28 |
29 | h1, h2, h3, h4 {
30 | font-weight: bold;
31 | margin: 0 0 15px;
32 | padding: 0;
33 | }
34 |
35 | h1 {
36 | border-bottom: 1px solid #ddd;
37 | font-size: 2.5em;
38 | }
39 |
40 | h2 {
41 | border-bottom: 1px solid #eee;
42 | font-size: 2em;
43 | }
44 |
45 | h3 {
46 | font-size: 1.5em;
47 | }
48 |
49 | h4 {
50 | font-size: 1.2em;
51 | }
52 |
53 | p, ul {
54 | margin: 15px 0;
55 | }
56 |
57 | ul {
58 | padding-left: 30px;
59 | }
60 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://heroku.com/deploy)
2 |
3 | # React Tutorial
4 |
5 | This is the React comment box example from [the React tutorial](http://facebook.github.io/react/docs/tutorial.html).
6 |
7 | ## To use
8 |
9 | There are several simple server implementations included. They all serve static files from `public/` and handle requests to `/api/comments` to fetch or add data. Start a server with one of the following:
10 |
11 | ### Node
12 |
13 | ```sh
14 | npm install
15 | node server.js
16 | ```
17 |
18 | ### Python
19 |
20 | ```sh
21 | pip install -r requirements.txt
22 | python server.py
23 | ```
24 |
25 | ### Ruby
26 | ```sh
27 | ruby server.rb
28 | ```
29 |
30 | ### PHP
31 | ```sh
32 | php server.php
33 | ```
34 |
35 | ### Go
36 | ```sh
37 | go run server.go
38 | ```
39 |
40 | ### Perl
41 |
42 | ```sh
43 | cpan Mojolicious
44 | perl server.pl
45 | ```
46 |
47 | And visit . Try opening multiple tabs!
48 |
49 | ## Changing the port
50 |
51 | You can change the port number by setting the `$PORT` environment variable before invoking any of the scripts above, e.g.,
52 |
53 | ```sh
54 | PORT=3001 node server.js
55 | ```
56 |
--------------------------------------------------------------------------------
/server.pl:
--------------------------------------------------------------------------------
1 | # This file provided by Facebook is for non-commercial testing and evaluation
2 | # purposes only. Facebook reserves all rights not expressly granted.
3 | #
4 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
7 | # FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
8 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
9 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
11 | use Time::HiRes qw(gettimeofday);
12 | use Mojolicious::Lite;
13 | use Mojo::JSON qw(encode_json decode_json);
14 |
15 | app->static->paths->[0] = './public';
16 |
17 | any '/' => sub { $_[0]->reply->static('index.html') };
18 |
19 | any [qw(GET POST)] => '/api/comments' => sub {
20 | my $self = shift;
21 | my $comments = decode_json (do { local(@ARGV,$/) = 'comments.json';<> });
22 | $self->res->headers->cache_control('no-cache');
23 | $self->res->headers->access_control_allow_origin('*');
24 |
25 | if ($self->req->method eq 'POST')
26 | {
27 | push @$comments, {
28 | id => int(gettimeofday * 1000),
29 | author => $self->param('author'),
30 | text => $self->param('text'),
31 | };
32 | open my $FILE, '>', 'comments.json';
33 | print $FILE encode_json($comments);
34 | }
35 | $self->render(json => $comments);
36 | };
37 | my $port = $ENV{PORT} || 3000;
38 | app->start('daemon', '-l', "http://*:$port");
39 |
--------------------------------------------------------------------------------
/server.py:
--------------------------------------------------------------------------------
1 | # This file provided by Facebook is for non-commercial testing and evaluation
2 | # purposes only. Facebook reserves all rights not expressly granted.
3 | #
4 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
7 | # FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
8 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
9 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
11 | import json
12 | import os
13 | import time
14 | from flask import Flask, Response, request
15 |
16 | app = Flask(__name__, static_url_path='', static_folder='public')
17 | app.add_url_rule('/', 'root', lambda: app.send_static_file('index.html'))
18 |
19 |
20 | @app.route('/api/comments', methods=['GET', 'POST'])
21 | def comments_handler():
22 | with open('comments.json', 'r') as f:
23 | comments = json.loads(f.read())
24 |
25 | if request.method == 'POST':
26 | new_comment = request.form.to_dict()
27 | new_comment['id'] = int(time.time() * 1000)
28 | comments.append(new_comment)
29 |
30 | with open('comments.json', 'w') as f:
31 | f.write(json.dumps(comments, indent=4, separators=(',', ': ')))
32 |
33 | return Response(
34 | json.dumps(comments),
35 | mimetype='application/json',
36 | headers={
37 | 'Cache-Control': 'no-cache',
38 | 'Access-Control-Allow-Origin': '*'
39 | }
40 | )
41 |
42 |
43 | if __name__ == '__main__':
44 | app.run(port=int(os.environ.get("PORT", 3000)), debug=True)
45 |
--------------------------------------------------------------------------------
/server.rb:
--------------------------------------------------------------------------------
1 | # This file provided by Facebook is for non-commercial testing and evaluation
2 | # purposes only. Facebook reserves all rights not expressly granted.
3 | #
4 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
7 | # FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
8 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
9 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
11 | require 'webrick'
12 | require 'json'
13 |
14 | # default port to 3000 or overwrite with PORT variable by running
15 | # $ PORT=3001 ruby server.rb
16 | port = ENV['PORT'] ? ENV['PORT'].to_i : 3000
17 |
18 | puts "Server started: http://localhost:#{port}/"
19 |
20 | root = File.expand_path './public'
21 | server = WEBrick::HTTPServer.new Port: port, DocumentRoot: root
22 |
23 | server.mount_proc '/api/comments' do |req, res|
24 | comments = JSON.parse(File.read('./comments.json', encoding: 'UTF-8'))
25 |
26 | if req.request_method == 'POST'
27 | # Assume it's well formed
28 | comment = { id: (Time.now.to_f * 1000).to_i }
29 | req.query.each do |key, value|
30 | comment[key] = value.force_encoding('UTF-8') unless key == 'id'
31 | end
32 | comments << comment
33 | File.write(
34 | './comments.json',
35 | JSON.pretty_generate(comments, indent: ' '),
36 | encoding: 'UTF-8'
37 | )
38 | end
39 |
40 | # always return json
41 | res['Content-Type'] = 'application/json'
42 | res['Cache-Control'] = 'no-cache'
43 | res['Access-Control-Allow-Origin'] = '*'
44 | res.body = JSON.generate(comments)
45 | end
46 |
47 | trap('INT') { server.shutdown }
48 |
49 | server.start
50 |
--------------------------------------------------------------------------------
/server.php:
--------------------------------------------------------------------------------
1 | round(microtime(true) * 1000),
39 | 'author' => $_POST['author'],
40 | 'text' => $_POST['text']
41 | ];
42 |
43 | $comments = json_encode($commentsDecoded, JSON_PRETTY_PRINT);
44 | file_put_contents('comments.json', $comments);
45 | }
46 | header('Content-Type: application/json');
47 | header('Cache-Control: no-cache');
48 | header('Access-Control-Allow-Origin: *');
49 | echo $comments;
50 | } else {
51 | return false;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file provided by Facebook is for non-commercial testing and evaluation
3 | * purposes only. Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | var fs = require('fs');
14 | var path = require('path');
15 | var express = require('express');
16 | var bodyParser = require('body-parser');
17 | var app = express();
18 |
19 | var COMMENTS_FILE = path.join(__dirname, 'comments.json');
20 |
21 | app.set('port', (process.env.PORT || 3000));
22 |
23 | app.use('/', express.static(path.join(__dirname, 'public')));
24 | app.use(bodyParser.json());
25 | app.use(bodyParser.urlencoded({extended: true}));
26 |
27 | // Additional middleware which will set headers that we need on each request.
28 | app.use(function(req, res, next) {
29 | // Set permissive CORS header - this allows this server to be used only as
30 | // an API server in conjunction with something like webpack-dev-server.
31 | res.setHeader('Access-Control-Allow-Origin', '*');
32 |
33 | // Disable caching so we'll always get the latest comments.
34 | res.setHeader('Cache-Control', 'no-cache');
35 | next();
36 | });
37 |
38 | app.get('/api/comments', function(req, res) {
39 | fs.readFile(COMMENTS_FILE, function(err, data) {
40 | if (err) {
41 | console.error(err);
42 | process.exit(1);
43 | }
44 | res.json(JSON.parse(data));
45 | });
46 | });
47 |
48 | app.post('/api/comments', function(req, res) {
49 | fs.readFile(COMMENTS_FILE, function(err, data) {
50 | if (err) {
51 | console.error(err);
52 | process.exit(1);
53 | }
54 | var comments = JSON.parse(data);
55 | // NOTE: In a real implementation, we would likely rely on a database or
56 | // some other approach (e.g. UUIDs) to ensure a globally unique id. We'll
57 | // treat Date.now() as unique-enough for our purposes.
58 | var newComment = {
59 | id: Date.now(),
60 | author: req.body.author,
61 | text: req.body.text,
62 | };
63 | comments.push(newComment);
64 | fs.writeFile(COMMENTS_FILE, JSON.stringify(comments, null, 4), function(err) {
65 | if (err) {
66 | console.error(err);
67 | process.exit(1);
68 | }
69 | res.json(comments);
70 | });
71 | });
72 | });
73 |
74 |
75 | app.listen(app.get('port'), function() {
76 | console.log('Server started: http://localhost:' + app.get('port') + '/');
77 | });
78 |
--------------------------------------------------------------------------------
/server.go:
--------------------------------------------------------------------------------
1 | /**
2 | * This file provided by Facebook is for non-commercial testing and evaluation
3 | * purposes only. Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | package main
14 |
15 | import (
16 | "bytes"
17 | "encoding/json"
18 | "fmt"
19 | "io"
20 | "io/ioutil"
21 | "log"
22 | "net/http"
23 | "os"
24 | "sync"
25 | "time"
26 | )
27 |
28 | type comment struct {
29 | ID int64 `json:"id"`
30 | Author string `json:"author"`
31 | Text string `json:"text"`
32 | }
33 |
34 | const dataFile = "./comments.json"
35 |
36 | var commentMutex = new(sync.Mutex)
37 |
38 | // Handle comments
39 | func handleComments(w http.ResponseWriter, r *http.Request) {
40 | // Since multiple requests could come in at once, ensure we have a lock
41 | // around all file operations
42 | commentMutex.Lock()
43 | defer commentMutex.Unlock()
44 |
45 | // Stat the file, so we can find its current permissions
46 | fi, err := os.Stat(dataFile)
47 | if err != nil {
48 | http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
49 | return
50 | }
51 |
52 | // Read the comments from the file.
53 | commentData, err := ioutil.ReadFile(dataFile)
54 | if err != nil {
55 | http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
56 | return
57 | }
58 |
59 | switch r.Method {
60 | case "POST":
61 | // Decode the JSON data
62 | var comments []comment
63 | if err := json.Unmarshal(commentData, &comments); err != nil {
64 | http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError)
65 | return
66 | }
67 |
68 | // Add a new comment to the in memory slice of comments
69 | comments = append(comments, comment{ID: time.Now().UnixNano() / 1000000, Author: r.FormValue("author"), Text: r.FormValue("text")})
70 |
71 | // Marshal the comments to indented json.
72 | commentData, err = json.MarshalIndent(comments, "", " ")
73 | if err != nil {
74 | http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError)
75 | return
76 | }
77 |
78 | // Write out the comments to the file, preserving permissions
79 | err := ioutil.WriteFile(dataFile, commentData, fi.Mode())
80 | if err != nil {
81 | http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError)
82 | return
83 | }
84 |
85 | w.Header().Set("Content-Type", "application/json")
86 | w.Header().Set("Cache-Control", "no-cache")
87 | w.Header().Set("Access-Control-Allow-Origin", "*")
88 | io.Copy(w, bytes.NewReader(commentData))
89 |
90 | case "GET":
91 | w.Header().Set("Content-Type", "application/json")
92 | w.Header().Set("Cache-Control", "no-cache")
93 | w.Header().Set("Access-Control-Allow-Origin", "*")
94 | // stream the contents of the file to the response
95 | io.Copy(w, bytes.NewReader(commentData))
96 |
97 | default:
98 | // Don't know the method, so error
99 | http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed)
100 | }
101 | }
102 |
103 | func main() {
104 | port := os.Getenv("PORT")
105 | if port == "" {
106 | port = "3000"
107 | }
108 | http.HandleFunc("/api/comments", handleComments)
109 | http.Handle("/", http.FileServer(http.Dir("./public")))
110 | log.Println("Server started: http://localhost:" + port)
111 | log.Fatal(http.ListenAndServe(":"+port, nil))
112 | }
113 |
--------------------------------------------------------------------------------
/public/scripts/example.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file provided by Facebook is for non-commercial testing and evaluation
3 | * purposes only. Facebook reserves all rights not expressly granted.
4 | *
5 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
7 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
8 | * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
9 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
10 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | */
12 |
13 | var Comment = React.createClass({
14 | rawMarkup: function() {
15 | var md = new Remarkable();
16 | var rawMarkup = md.render(this.props.children.toString());
17 | return { __html: rawMarkup };
18 | },
19 |
20 | render: function() {
21 | return (
22 |
23 |
24 | {this.props.author}
25 |
26 |
27 |
28 | );
29 | }
30 | });
31 |
32 | var CommentBox = React.createClass({
33 | loadCommentsFromServer: function() {
34 | $.ajax({
35 | url: this.props.url,
36 | dataType: 'json',
37 | cache: false,
38 | success: function(data) {
39 | this.setState({data: data});
40 | }.bind(this),
41 | error: function(xhr, status, err) {
42 | console.error(this.props.url, status, err.toString());
43 | }.bind(this)
44 | });
45 | },
46 | handleCommentSubmit: function(comment) {
47 | var comments = this.state.data;
48 | // Optimistically set an id on the new comment. It will be replaced by an
49 | // id generated by the server. In a production application you would likely
50 | // not use Date.now() for this and would have a more robust system in place.
51 | comment.id = Date.now();
52 | var newComments = comments.concat([comment]);
53 | this.setState({data: newComments});
54 | $.ajax({
55 | url: this.props.url,
56 | dataType: 'json',
57 | type: 'POST',
58 | data: comment,
59 | success: function(data) {
60 | this.setState({data: data});
61 | }.bind(this),
62 | error: function(xhr, status, err) {
63 | this.setState({data: comments});
64 | console.error(this.props.url, status, err.toString());
65 | }.bind(this)
66 | });
67 | },
68 | getInitialState: function() {
69 | return {data: []};
70 | },
71 | componentDidMount: function() {
72 | this.loadCommentsFromServer();
73 | setInterval(this.loadCommentsFromServer, this.props.pollInterval);
74 | },
75 | render: function() {
76 | return (
77 |
78 |
Comments
79 |
80 |
81 |
82 | );
83 | }
84 | });
85 |
86 | var CommentList = React.createClass({
87 | render: function() {
88 | var commentNodes = this.props.data.map(function(comment) {
89 | return (
90 |
91 | {comment.text}
92 |
93 | );
94 | });
95 | return (
96 |
97 | {commentNodes}
98 |
99 | );
100 | }
101 | });
102 |
103 | var CommentForm = React.createClass({
104 | getInitialState: function() {
105 | return {author: '', text: ''};
106 | },
107 | handleAuthorChange: function(e) {
108 | this.setState({author: e.target.value});
109 | },
110 | handleTextChange: function(e) {
111 | this.setState({text: e.target.value});
112 | },
113 | handleSubmit: function(e) {
114 | e.preventDefault();
115 | var author = this.state.author.trim();
116 | var text = this.state.text.trim();
117 | if (!text || !author) {
118 | return;
119 | }
120 | this.props.onCommentSubmit({author: author, text: text});
121 | this.setState({author: '', text: ''});
122 | },
123 | render: function() {
124 | return (
125 |
140 | );
141 | }
142 | });
143 |
144 | ReactDOM.render(
145 | ,
146 | document.getElementById('content')
147 | );
148 |
--------------------------------------------------------------------------------