Переглянути джерело

Step 2: static version: Initial version = empty table.

Frederic G. MARAND 9 роки тому
батько
коміт
24c3f635c8
8 змінених файлів з 89 додано та 443 видалено
  1. 5 19
      README.md
  2. 0 12
      comments.json
  3. 4 8
      package.json
  4. 3 3
      public/index.html
  5. 0 146
      public/scripts/example.js
  6. 77 0
      public/scripts/thinking.js
  7. 0 141
      public/scripts/tutorial.js
  8. 0 114
      server.go

+ 5 - 19
README.md

@@ -1,25 +1,11 @@
-# react-intro : Learning ReactJS
+# react-intro : Thinking in ReactJS
 
-This is the React comment box example from [the React tutorial](http://facebook.github.io/react/docs/tutorial.html).
+This is the React Thinking React JSON example from the
+[Thiking in React tutorial](https://facebook.github.io/react/docs/thinking-in-react.html).
 
 ## Installing
 
-```sh
-go run server.go
-```
+Run some server like `python -m SimpleHTTPServer`
 
-And visit <http://vm:3000/> assuming you host is called `vm`. Try opening multiple tabs!
+And visit <http://vm:3000/> assuming you host is called `vm`.
 
-## Changing the port
-
-You can change the port number by setting the `$PORT` environment variable before invoking any of the scripts above, e.g.,
-
-```sh
-PORT=3001 node server.js
-```
-
-## Parts taken from:
-
-- https://facebook.github.io/react/docs/tutorial.html
-- http://jslog.com/2014/10/02/react-with-webpack-part-1/ : much more useful than
-  ReactJS "Getting started".

+ 0 - 12
comments.json

@@ -1,12 +0,0 @@
-[
-    {
-        "id": 1388534400000,
-        "author": "Pete Hunt",
-        "text": "Hey there!"
-    },
-    {
-        "id": 1420070400000,
-        "author": "Paul O’Shannessy",
-        "text": "React is *great*!"
-    }
-]

+ 4 - 8
package.json

@@ -1,16 +1,12 @@
 {
   "name": "react-tutorial",
   "version": "0.0.0",
-  "description": "Code from the React tutorial.",
-  "main": "server.js",
+  "description": "Code from the Thinking in React tutorial.",
   "dependencies": {
-    "body-parser": "^1.4.3",
-    "express": "^4.4.5"
   },
   "devDependencies": {},
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1",
-    "start": "node server.js"
+    "test": "echo \"Error: no test specified\" && exit 1"
   },
   "repository": {
     "type": "git",
@@ -23,11 +19,11 @@
     "example"
   ],
   "author": "petehunt",
-  "license": "proprietary",
+  "license": "UNLICENSED",
   "bugs": {
     "url": "https://github.com/reactjs/react-tutorial/issues"
   },
-  "homepage": "https://github.com/reactjs/react-tutorial",
+  "homepage": "https://facebook.github.io/react/docs/thinking-in-react.html",
   "engines" : {
     "node" : "0.12.x"
   }

+ 3 - 3
public/index.html

@@ -3,6 +3,7 @@
   <head>
     <meta charset="utf-8" />
     <title>React Tutorial</title>
+    <link rel="stylesheet" type="text/css" href="css/base.css" />
     <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
@@ -11,8 +12,7 @@
   </head>
 
   <body>
-    <div id="content"></div>
-    <!-- script type="text/babel" src="scripts/example.js"></script -->
-    <script type="text/babel" src="scripts/tutorial.js"></script>
+    <div id="content">Initial body content</div>
+    <script type="text/babel" src="scripts/thinking.js"></script>
   </body>
 </html>

+ 0 - 146
public/scripts/example.js

@@ -1,146 +0,0 @@
-/**
- * This file provided by Facebook is for non-commercial testing and evaluation
- * purposes only. Facebook reserves all rights not expressly granted.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-var Comment = React.createClass({
-  rawMarkup: function() {
-    var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
-    return { __html: rawMarkup };
-  },
-
-  render: function() {
-    return (
-      <div className="comment">
-        <h2 className="commentAuthor">
-          {this.props.author}
-        </h2>
-        <span dangerouslySetInnerHTML={this.rawMarkup()} />
-      </div>
-    );
-  }
-});
-
-var CommentBox = React.createClass({
-  loadCommentsFromServer: function() {
-    $.ajax({
-      url: this.props.url,
-      dataType: 'json',
-      cache: false,
-      success: function(data) {
-        this.setState({data: data});
-      }.bind(this),
-      error: function(xhr, status, err) {
-        console.error(this.props.url, status, err.toString());
-      }.bind(this)
-    });
-  },
-  handleCommentSubmit: function(comment) {
-    var comments = this.state.data;
-    // Optimistically set an id on the new comment. It will be replaced by an
-    // id generated by the server. In a production application you would likely
-    // not use Date.now() for this and would have a more robust system in place.
-    comment.id = Date.now();
-    var newComments = comments.concat([comment]);
-    this.setState({data: newComments});
-    $.ajax({
-      url: this.props.url,
-      dataType: 'json',
-      type: 'POST',
-      data: comment,
-      success: function(data) {
-        this.setState({data: data});
-      }.bind(this),
-      error: function(xhr, status, err) {
-        this.setState({data: comments});
-        console.error(this.props.url, status, err.toString());
-      }.bind(this)
-    });
-  },
-  getInitialState: function() {
-    return {data: []};
-  },
-  componentDidMount: function() {
-    this.loadCommentsFromServer();
-    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
-  },
-  render: function() {
-    return (
-      <div className="commentBox">
-        <h1>Comments</h1>
-        <CommentList data={this.state.data} />
-        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
-      </div>
-    );
-  }
-});
-
-var CommentList = React.createClass({
-  render: function() {
-    var commentNodes = this.props.data.map(function(comment) {
-      return (
-        <Comment author={comment.author} key={comment.id}>
-          {comment.text}
-        </Comment>
-      );
-    });
-    return (
-      <div className="commentList">
-        {commentNodes}
-      </div>
-    );
-  }
-});
-
-var CommentForm = React.createClass({
-  getInitialState: function() {
-    return {author: '', text: ''};
-  },
-  handleAuthorChange: function(e) {
-    this.setState({author: e.target.value});
-  },
-  handleTextChange: function(e) {
-    this.setState({text: e.target.value});
-  },
-  handleSubmit: function(e) {
-    e.preventDefault();
-    var author = this.state.author.trim();
-    var text = this.state.text.trim();
-    if (!text || !author) {
-      return;
-    }
-    this.props.onCommentSubmit({author: author, text: text});
-    this.setState({author: '', text: ''});
-  },
-  render: function() {
-    return (
-      <form className="commentForm" onSubmit={this.handleSubmit}>
-        <input
-          type="text"
-          placeholder="Your name"
-          value={this.state.author}
-          onChange={this.handleAuthorChange}
-        />
-        <input
-          type="text"
-          placeholder="Say something..."
-          value={this.state.text}
-          onChange={this.handleTextChange}
-        />
-        <input type="submit" value="Post" />
-      </form>
-    );
-  }
-});
-
-ReactDOM.render(
-  <CommentBox url="/api/comments" pollInterval={2000} />,
-  document.getElementById('content')
-);

+ 77 - 0
public/scripts/thinking.js

@@ -0,0 +1,77 @@
+let ProductCategoryRow = React.createClass({
+  name: "ProductCategoryRow",
+
+  componentDidMount: function () {
+    console.log("Component did mountDM", this.name);
+  },
+  getInitialState: function () {
+    console.log("Get Initial State", this.name);
+    return { data: [] };
+  },
+  render:  function () {
+    return (<tr><th colSpan="2">{this.props.category}</th></tr>);
+  }
+});
+
+let ProductRow = React.createClass({
+  name: "ProductRow",
+
+  componentDidMount: function () {
+    console.log("Component did mountDM", this.name);
+  },
+  getInitialState: function () {
+    console.log("Get Initial State", this.name);
+    return { data: [] };
+  },
+  render: function () {
+    let name = this.props.product.stocked
+      ? this.props.product.name
+      : <span style={{color: 'red'}}>{this.props.product.name}</span>;
+    return (
+      <tr>
+        <td>{name}</td>
+        <td>{this.props.product.price}</td>
+      </tr>
+    );
+  }
+});
+
+let ProductTable = React.createClass({
+  name: "ProductTable",
+  componentDidMount: function () {
+    console.log("Component did mountDM", this.name);
+  },
+  getInitialState: function () {
+    console.log("Get Initial State", this.name);
+    return { data: [] };
+  },
+  render: function () {
+    let rows = [];
+    let lastCategory = null;
+    this.props.products.forEach(function (product) {
+      if (product.category !== lastCategory) {
+        rows.push(<ProductCategoryRow category={product.category} key={product.category} />);
+      }
+      rows.push(<ProductRow product={product} key={product.name} />);
+      lastCategory = product.category;
+    });
+    return (
+      <table>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Price</th>
+          </tr>
+        </thead>
+        <tbody>
+        {rows}
+        </tbody>
+      </table>
+    );
+  }
+});
+
+ReactDOM.render(
+  <ProductTable products={[]} />,
+  document.getElementById("content")
+);

+ 0 - 141
public/scripts/tutorial.js

@@ -1,141 +0,0 @@
-let Comment = React.createClass({
-  rawMarkup: function () {
-    let rawMarkup = marked(this.props.children.toString(), { sanitize: true });
-    return { __html: rawMarkup };
-  },
-  render: function () {
-    return (
-      <div className="comment">
-        <h2 className="commentAuthor">
-          {this.props.author}
-        </h2>
-        <span dangerouslySetInnerHTML={this.rawMarkup()} />
-      </div>
-    );
-  }
-});
-
-let CommentList = React.createClass({
-  render: function () {
-    let commentNodes = this.props.data.map(function (comment) {
-      return (
-        <Comment author={comment.author} key={comment.id}>
-          {comment.text}
-        </Comment>
-      );
-    });
-    return (
-      <div className="commentList">
-        {commentNodes}
-        </div>
-    );
-  }
-});
-
-let CommentForm = React.createClass({
-  getInitialState: function () {
-    return {
-      author: "",
-      text: ""
-    };
-  },
-  handleAuthorChange: function (e) {
-    this.setState({ author: e.target.value });
-  },
-  handleSubmit: function (e) {
-    e.preventDefault();
-    let author = this.state.author.trim();
-    let text = this.state.text.trim();
-    if (!text || !author) {
-      return;
-    }
-    this.props.onCommentSubmit({ author: author, text: text });
-    this.setState({ author: '', text: '' });
-  },
-  handleTextChange: function (e) {
-    this.setState({ text: e.target.value });
-  },
-  render: function () {
-    return (
-      <div className="commentForm">
-        <form className="commentForm" onSubmit={this.handleSubmit}>
-          <input
-            type="text"
-            placeholder="Your name"
-            value={this.state.author}
-            onChange={this.handleAuthorChange}
-          />
-          <input
-            type="text"
-            placeholder="Say something..."
-            value={this.state.text}
-            onChange={this.handleTextChange}
-          />
-          <input type="submit" value="Post" />
-        </form>
-      </div>
-    );
-  }
-});
-
-let CommentBox = React.createClass({
-  componentDidMount: function () {
-    this.loadCommentsFromServer();
-    setInterval(this.loadCommentsFromServer, this.props.pollInterval);
-  },
-  getInitialState: function () {
-    return { data: [] };
-  },
-  handleCommentSubmit: function (comment) {
-    let comments = this.state.data;
-    // Optimistically set an id on the new comment. It will be replaced by an
-    // id generated on the server. In a production application, you would likely
-    // not use Date.now() for this and would have a more robust system in place.
-    comment.id = Date.now();
-    let newComments = comments.concat([comment]);
-    // Assume all will go well.
-    this.setState({ data: newComments });
-
-    $.ajax({
-      url: this.props.url,
-      dataType: "json",
-      type: "POST",
-      data: comment,
-      success: function (data) {
-        this.setState({ data: data });
-      }.bind(this),
-      error: function (xhr, status, err) {
-        // Restore the original comment state since the new state could not be applied.
-        this.setState({data: comments });
-        console.error(this.props.url, status, err.toString());
-      }
-    });
-  },
-  loadCommentsFromServer: function () {
-    $.ajax({
-      url: this.props.url,
-      dataType: "json",
-      cache: false,
-      success: function (result) {
-        this.setState({ data: result });
-      }.bind(this),
-      error: function (xhr, status, err) {
-        console.error(this.props.url, status, err.toString());
-      }.bind(this)
-    });
-  },
-  render: function () {
-    return (
-      <div className="commentBox">
-        <h1>Comments</h1>
-        <CommentList data={this.state.data} />
-        <CommentForm onCommentSubmit={this.handleCommentSubmit} />
-        </div>
-    );
-  }
-});
-
-ReactDOM.render(
-  <CommentBox url="/api/comments" pollInterval={2000} />,
-  document.getElementById("content")
-);

+ 0 - 114
server.go

@@ -1,114 +0,0 @@
-/**
- * This file provided by Facebook is for non-commercial testing and evaluation
- * purposes only. Facebook reserves all rights not expressly granted.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package main
-
-import (
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"log"
-	"net/http"
-	"os"
-	"sync"
-	"time"
-)
-
-type comment struct {
-	ID     int64  `json:"id"`
-	Author string `json:"author"`
-	Text   string `json:"text"`
-}
-
-const dataFile = "./comments.json"
-
-var commentMutex = new(sync.Mutex)
-
-func updateComments(w http.ResponseWriter, r *http.Request, commentData []byte, fi os.FileInfo) {
-	// Decode the JSON data
-	var comments []comment
-	if err := json.Unmarshal(commentData, &comments); err != nil {
-		http.Error(w, fmt.Sprintf("Unable to Unmarshal comments from data file (%s): %s", dataFile, err), http.StatusInternalServerError)
-		return
-	}
-
-	// Add a new comment to the in memory slice of comments
-	comments = append(comments, comment{ID: time.Now().UnixNano() / 1000000, Author: r.FormValue("author"), Text: r.FormValue("text")})
-
-	// Marshal the comments to indented json.
-	commentData, err := json.MarshalIndent(comments, "", "    ")
-	if err != nil {
-		http.Error(w, fmt.Sprintf("Unable to marshal comments to json: %s", err), http.StatusInternalServerError)
-		return
-	}
-
-	// Write out the comments to the file, preserving permissions.
-	err = ioutil.WriteFile(dataFile, commentData, fi.Mode())
-	if err != nil {
-		http.Error(w, fmt.Sprintf("Unable to write comments to data file (%s): %s", dataFile, err), http.StatusInternalServerError)
-		return
-	}
-
-	w.Header().Set("Content-Type", "application/json")
-	w.Header().Set("Cache-Control", "no-cache")
-	io.Copy(w, bytes.NewReader(commentData))
-}
-
-// Handle comments
-func handleComments(w http.ResponseWriter, r *http.Request) {
-	// Since multiple requests could come in at once, ensure we have a lock
-	// around all file operations
-	commentMutex.Lock()
-	defer commentMutex.Unlock()
-
-	// Stat the file, so we can find its current permissions
-	fi, err := os.Stat(dataFile)
-	if err != nil {
-		http.Error(w, fmt.Sprintf("Unable to stat the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
-		return
-	}
-
-	// Read the comments from the file.
-	commentData, err := ioutil.ReadFile(dataFile)
-	if err != nil {
-		http.Error(w, fmt.Sprintf("Unable to read the data file (%s): %s", dataFile, err), http.StatusInternalServerError)
-		return
-	}
-
-	switch r.Method {
-	case "POST":
-		updateComments(w, r, commentData, fi);
-
-	case "GET":
-		w.Header().Set("Content-Type", "application/json")
-		w.Header().Set("Cache-Control", "no-cache")
-		// stream the contents of the file to the response
-		io.Copy(w, bytes.NewReader(commentData))
-
-	default:
-		// Don't know the method, so error.
-		http.Error(w, fmt.Sprintf("Unsupported method: %s", r.Method), http.StatusMethodNotAllowed)
-	}
-}
-
-func main() {
-	port := os.Getenv("PORT")
-	if port == "" {
-		port = "3000"
-	}
-	http.HandleFunc("/api/comments", handleComments)
-	http.Handle("/", http.FileServer(http.Dir("./public")))
-	log.Println("Server started: http://localhost:" + port)
-	log.Fatal(http.ListenAndServe(":" + port, nil))
-}