├── requirements.txt
├── .gitignore
├── comments.json
├── .editorconfig
├── app.json
├── LICENSE
├── public
├── scripts
│ ├── polyfill.js
│ └── example.js
├── css
│ └── base.css
└── index.html
├── package.json
├── 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 Redux Tutorial Server",
3 | "description": "Code from the React tutorial, modified to use basic Redux",
4 | "keywords": [ "react", "reactjs", "redux", "tutorial" ],
5 | "repository": "https://github.com/firasd/react-redux-tutorial",
6 | "logo": "https://facebook.github.io/react/img/logo.svg",
7 | "website": "https://github.com/firasd/react-redux-tutorial",
8 | "success_url": "/",
9 | "env" : {
10 | "BUILDPACK_URL": "https://github.com/heroku/heroku-buildpack-nodejs.git"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/public/scripts/polyfill.js:
--------------------------------------------------------------------------------
1 | if (typeof Object.assign != 'function') {
2 | (function () {
3 | Object.assign = function (target) {
4 | 'use strict';
5 | if (target === undefined || target === null) {
6 | throw new TypeError('Cannot convert undefined or null to object');
7 | }
8 |
9 | var output = Object(target);
10 | for (var index = 1; index < arguments.length; index++) {
11 | var source = arguments[index];
12 | if (source !== undefined && source !== null) {
13 | for (var nextKey in source) {
14 | if (source.hasOwnProperty(nextKey)) {
15 | output[nextKey] = source[nextKey];
16 | }
17 | }
18 | }
19 | }
20 | return output;
21 | };
22 | })();
23 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-redux-tutorial",
3 | "version": "0.0.0",
4 | "description": "Code from the React tutorial, modified to use basic Redux.",
5 | "main": "server.js",
6 | "dependencies": {
7 | "body-parser": "^1.4.3",
8 | "express": "^4.4.5"
9 | },
10 | "devDependencies": {},
11 | "scripts": {
12 | "test": "echo \"Error: no test specified\" && exit 1",
13 | "start": "node server.js"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/firasd/react-redux-tutorial.git"
18 | },
19 | "keywords": [
20 | "react",
21 | "redux",
22 | "tutorial",
23 | "comment",
24 | "example"
25 | ],
26 | "author": "petehunt",
27 | "bugs": {
28 | "url": "https://github.com/firasd/react-redux-tutorial/issues"
29 | },
30 | "homepage": "https://github.com/firasd/react-redux-tutorial",
31 | "engines" : {
32 | "node" : "0.12.x"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/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 | font-weight: bold;
39 | margin: 0 0 15px;
40 | padding: 0;
41 | }
42 |
43 | h2 {
44 | border-bottom: 1px solid #eee;
45 | font-size: 2em;
46 | }
47 |
48 | h3 {
49 | font-size: 1.5em;
50 | }
51 |
52 | h4 {
53 | font-size: 1.2em;
54 | }
55 |
56 | p, ul {
57 | margin: 15px 0;
58 | }
59 |
60 | ul {
61 | padding-left: 30px;
62 | }
63 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://heroku.com/deploy)
2 |
3 | # Simple React Redux Tutorial
4 |
5 |
6 | This is a modified version of the React comment box example from [the React tutorial](http://facebook.github.io/react/docs/tutorial.html). The example app has been modified to use Redux.
7 |
8 | ## To use
9 |
10 | 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:
11 |
12 | ### Node
13 |
14 | ```sh
15 | npm install
16 | node server.js
17 | ```
18 |
19 | ### Python
20 |
21 | ```sh
22 | pip install -r requirements.txt
23 | python server.py
24 | ```
25 |
26 | ### Ruby
27 | ```sh
28 | ruby server.rb
29 | ```
30 |
31 | ### PHP
32 | ```sh
33 | php server.php
34 | ```
35 |
36 | ### Go
37 | ```sh
38 | go run server.go
39 | ```
40 |
41 | ### Perl
42 |
43 | ```sh
44 | cpan Mojolicious
45 | perl server.pl
46 | ```
47 |
48 | And visit . Try opening multiple tabs!
49 |
50 | ## Changing the port
51 |
52 | You can change the port number by setting the `$PORT` environment variable before invoking any of the scripts above, e.g.,
53 |
54 | ```sh
55 | PORT=3001 node server.js
56 | ```
57 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React Tutorial
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/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 | @app.route('/api/comments', methods=['GET', 'POST'])
20 | def comments_handler():
21 |
22 | with open('comments.json', 'r') as file:
23 | comments = json.loads(file.read())
24 |
25 | if request.method == 'POST':
26 | newComment = request.form.to_dict()
27 | newComment['id'] = int(time.time() * 1000)
28 | comments.append(newComment)
29 |
30 | with open('comments.json', 'w') as file:
31 | file.write(json.dumps(comments, indent=4, separators=(',', ': ')))
32 |
33 | return Response(json.dumps(comments), mimetype='application/json', headers={'Cache-Control': 'no-cache', 'Access-Control-Allow-Origin': '*'})
34 |
35 | if __name__ == '__main__':
36 | app.run(port=int(os.environ.get("PORT",3000)))
37 |
--------------------------------------------------------------------------------
/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 rawMarkup = marked(this.props.children.toString(), {sanitize: true});
16 | return { __html: rawMarkup };
17 | },
18 |
19 | render: function() {
20 | return (
21 |
22 |
23 | {this.props.author}
24 |
25 |
26 |
27 | );
28 | }
29 | });
30 |
31 | var CommentBox = React.createClass({
32 | loadCommentsFromServer: function() {
33 | $.ajax({
34 | url: this.props.url,
35 | dataType: 'json',
36 | cache: false,
37 | success: function(data) {
38 | this.props.setComments(data);
39 | }.bind(this),
40 | error: function(xhr, status, err) {
41 | console.error(this.props.url, status, err.toString());
42 | }.bind(this)
43 | });
44 | },
45 | handleCommentSubmit: function(comment) {
46 | var comments = this.props.data;
47 | // Optimistically set an id on the new comment. It will be replaced by an
48 | // id generated by the server. In a production application you would likely
49 | // not use Date.now() for this and would have a more robust system in place.
50 | this.props.addComment(comment);
51 | $.ajax({
52 | url: this.props.url,
53 | dataType: 'json',
54 | type: 'POST',
55 | data: comment,
56 | success: function(data) {
57 | this.props.setComments(data);
58 | }.bind(this),
59 | error: function(xhr, status, err) {
60 | this.props.setComments(comments);
61 | console.error(this.props.url, status, err.toString());
62 | }.bind(this)
63 | });
64 | },
65 | componentDidMount: function() {
66 | this.loadCommentsFromServer();
67 | setInterval(this.loadCommentsFromServer, this.props.pollInterval);
68 | },
69 | render: function() {
70 | return (
71 |