* How to find out the phone numbers of nearby cellphones
* How to track the location of a cellphone that you only know the phone number of
* How intercept outgoing calls of nearby cellphones","time":1419715606,"title":"SS7: Locate. Track. Manipulate [video]","type":"story","url":"http://streaming.media.ccc.de/relive/6249/"},{"by":"jwatte","id":8803235,"kids":[8803652,8804123,8804262,8804421,8804287,8804144,8803910],"score":114,"text":"","time":1419702205,"title":"The Real-time Web: How to Get Millisecond Updates with REST","type":"story","url":"http://engineering.imvu.com/2014/12/27/the-real-time-web-in-rest-services-at-imvu/"},{"by":"pmoriarty","id":8803498,"kids":[8804214,8805055,8804229,8804041,8804008,8804753,8804990,8804173,8804042,8804997,8803959,8804450],"score":91,"text":"","time":1419706531,"title":"Masscan: Scan the entire Internet in under 5 minutes","type":"story","url":"https://github.com/robertdavidgraham/masscan"},{"by":"Thevet","id":8804296,"kids":[8804610,8805631,8804717,8804483,8804874,8804748,8804475,8804914,8804447,8804790,8805257,8804623,8805051],"score":41,"text":"","time":1419721241,"title":"Home for the holidays, and for a 20-year-old issue of PC Magazine","type":"story","url":"http://www.theverge.com/tldr/2014/12/26/7451295/home-for-the-holidays-and-for-a-20-year-old-issue-of-pc-magazine"},{"by":"lkrubner","id":8803101,"kids":[8805105,8805658,8804495,8803794,8804347,8805289,8805524,8804793,8804247,8804968,8804106,8805271,8804665,8805031,8804642,8805434,8804814,8803946,8804259,8804826,8804875,8804013,8803833],"score":89,"text":"","time":1419699685,"title":"When rational thinking is correlated with intelligence the correlation is modest","type":"story","url":"http://www.scientificamerican.com/article/rational-and-irrational-thought-the-thinking-that-iq-tests-miss/"},{"by":"ColinWright","id":8803138,"kids":[8805684,8803829,8805200,8803761,8804051,8804965,8804311],"score":96,"time":1419700378,"title":"Apollo 11 Flight Plan – Final [pdf]","type":"story","url":"https://www.hq.nasa.gov/alsj/a11/a11fltpln_final_reformat.pdf"},{"by":"r0h1n","id":8805394,"score":7,"text":"","time":1419744647,"title":"The Slow Death of ‘Do Not Track’","type":"story","url":"http://nytimes.com/2014/12/27/opinion/the-slow-death-of-do-not-track.html"},{"by":"waffle_ss","id":8804624,"kids":[8804895,8804788,8804923,8804784,8805077,8805691,8804729,8804989,8804931,8804763,8804940,8804780,8804764,8804868,8804956,8804982,8804935],"score":150,"text":"","time":1419727453,"title":"James Golick has died","type":"story","url":"https://twitter.com/jill380/status/548978785404874753"},{"by":"lelf","id":8802290,"kids":[8805536,8803376,8803757,8803307,8803438,8804667,8803619,8803647,8804264,8804609,8804218,8804222,8803633],"score":91,"time":1419678666,"title":"Patients do better when cardiologists are away at academic meetings","type":"story","url":"http://theincidentaleconomist.com/wordpress/patients-do-better-when-cardiologists-are-away-at-academic-meetings/"},{"by":"MichaelAO","id":8801616,"kids":[8801839,8803710,8802569,8801827,8802483,8804177,8801852,8801823,8802186,8802059,8802072,8803818,8802058,8801819,8802178,8801828,8801973],"score":240,"text":"","time":1419652028,"title":"Slow, flexible and cheap: Six years of development to create a rubber hexagon","type":"story","url":"https://medium.com/dome-kit/slow-flexible-cheap-5598ca91fb38"},{"by":"frostmatthew","id":8804934,"kids":[8805042,8805075,8805120,8805591,8805509,8805056,8805084,8805556,8805224,8805089,8805187,8805061,8805274,8805101,8805193,8805463,8805064,8805338,8805109,8805063,8805071,8805067],"score":165,"text":"","time":1419734117,"title":"On Immigration, Engineers Simply Don’t Trust VCs","type":"story","url":"http://techcrunch.com/2014/12/27/on-immigration-engineers-simply-dont-trust-vcs/"},{"by":"sanxiyn","id":8802425,"kids":[8805417,8802961,8803240],"score":116,"text":"","time":1419685288,"title":"Robdns \u2013 A fast DNS server based on C10M principles","type":"story","url":"https://github.com/robertdavidgraham/robdns"},{"by":"Petiver","id":8805487,"score":3,"text":"","time":1419747147,"title":"The Treasure of Nagyszentmiklós","type":"story","url":"http://en.wikipedia.org/wiki/Treasure_of_Nagyszentmikl%C3%B3s"},{"by":"carljoseph","id":8802485,"kids":[8803927,8805337,8803869,8803739,8804304,8804966,8804031,8803901,8804886,8803763,8805515,8803577,8804668,8804224],"score":76,"text":"","time":1419687337,"title":"The Software Scientist","type":"story","url":"http://www.evanmiller.org/the-software-scientist.html"},{"by":"Vigier","id":8804752,"score":7,"text":"","time":1419730398,"title":"House Perfect: Is the Ikea Ethos Comfy or Creepy? (2011)","type":"story","url":"http://www.newyorker.com/magazine/2011/10/03/house-perfect"},{"by":"sethbannon","id":8803844,"kids":[8805605,8805100,8804211,8804986,8804727,8804055,8804361,8805340,8804301,8804680,8804268,8804225,8804058,8804305,8804379],"score":59,"text":"","time":1419712871,"title":"When you have to wake up earlier than usual","type":"story","url":"http://blog.42floors.com/waking-up-early/"},{"by":"wslh","id":8804979,"score":5,"text":"","time":1419735352,"title":"How movies embraced Hinduism","type":"story","url":"http://www.theguardian.com/film/2014/dec/25/movies-embraced-hinduism"},{"by":"aestetix","id":8802138,"kids":[8802393,8802343,8802215,8802437,8802493,8803188,8802357,8804415,8802383,8802319,8802397,8803608,8802610,8802286,8802197,8802329,8802285,8802271,8802330],"score":157,"text":"","time":1419671428,"title":"31C3 Streaming: Saal 1 \u2013 SD Video","type":"story","url":"http://streaming.media.ccc.de/saal1/"},{"by":"diafygi","id":8803118,"kids":[8803351,8804151,8803613,8803234,8803409,8803466,8803454,8803396,8803395,8803413,8803381,8803427,8803926,8803416,8803369,8803587,8803402,8803974,8804261,8804322,8804245,8803140,8803340,8803424,8803897,8803357,8803363,8804917],"score":241,"text":"","time":1419700057,"title":"Many Android bugs with 500+ stars closed as obsolete on December 25","type":"story","url":"https://code.google.com/p/android/issues/list?can=1&q=status:Obsolete&sort=-stars"},{"by":"mrry","id":8802392,"kids":[8802474],"score":98,"text":"","time":1419684021,"title":"Functional Operating System and Security Protocol Engineering","type":"story","url":"http://decks.openmirage.org/31c3#/"}]
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | React HN
2 | ===
3 | This is a visual React tutorial. This tutorial should give you a feel for "growing" a React UI from small, modular parts. By the end of this tutorial, you will have built the [HN front page in React](https://mking.github.io/react-hn).
4 |
5 | > Note: This tutorial covers React, Browserify, and CSS. It does not cover event handling (not needed for the HN front page), state (not needed for the HN front page), or Flux.
6 |
7 | This tutorial has five parts:
8 |
9 | 1. [Setup](#setup)
10 |
11 | 1. [NewsItem component](#newsitem)
12 |
13 |
14 |
15 | 1. [NewsHeader component](#newsheader)
16 |
17 |
18 |
19 | 1. [NewsList component](#newslist)
20 |
21 |
22 |
23 | 1. [Display live data from the Hacker News API](#hacker-news-api)
24 |
25 | ---
26 |
27 | Setup
28 | ---
29 | 1. Create the project directory structure.
30 | ```bash
31 | mkdir -p hn/{build/js,css,html,img,js,json}
32 | cd hn
33 | ```
34 |
35 | > Note: We will be building the project from scratch. The solution in this repo is meant primarily to be a reference.
36 |
37 | 1. [Download the sample data](https://raw.githubusercontent.com/mking/react-hn/master/json/items.json) into /json.
38 |
39 | 1. Download [y18.gif](https://news.ycombinator.com/y18.gif) and [grayarrow2x.gif](https://news.ycombinator.com/grayarrow2x.gif) into /img.
40 |
41 | 1. Create /package.json.
42 | ```json
43 | {
44 | "name": "hn",
45 | "version": "0.1.0",
46 | "private": true,
47 | "browserify": {
48 | "transform": [
49 | ["reactify"]
50 | ]
51 | }
52 | }
53 | ```
54 |
55 | 1. Install Browserify, React, and tools.
56 | ```bash
57 | # These dependencies are required for running the app.
58 | npm install --save react jquery lodash moment
59 |
60 | # These dependencies are required for building the app.
61 | npm install --save-dev browserify watchify reactify
62 |
63 | # These dependencies are globally installed command line tools.
64 | npm install -g browserify watchify http-server
65 | ```
66 |
67 | [Next](#newsitem)
68 |
69 | ---
70 |
71 | NewsItem
72 | ---
73 | 1. [Display the title.](#newsitem-title)
74 |
75 |
76 |
77 | 1. [Add the domain.](#newsitem-domain)
78 |
79 |
80 |
81 | 1. [Add the subtext.](#newsitem-subtext)
82 |
83 |
84 |
85 | 1. [Add the rank and vote.](#newsitem-rank-and-vote)
86 |
87 |
88 |
89 | [Previous](#setup) · [Next](#newsitem-title)
90 |
91 | ---
92 |
93 | NewsItem Title
94 | ---
95 | 1. Create a new JS file: /js/NewsItem.js.
96 | ```javascript
97 | var $ = require('jquery');
98 | var React = require('react');
99 |
100 | var NewsItem = React.createClass({
101 | render: function () {
102 | return (
103 |
184 |
185 |
186 |
187 | [Previous](#newsitem) · [Next](#newsitem-domain)
188 |
189 | NewsItem Domain
190 | ---
191 | 1. Update the JS.
192 | ```javascript
193 | // ...
194 | var url = require('url');
195 |
196 | var NewsItem = React.createClass({
197 | // ...
198 |
199 | getDomain: function () {
200 | return url.parse(this.props.item.url).hostname;
201 | },
202 |
203 | render: function () {
204 | return (
205 |
230 |
231 | [Previous](#newsitem-title) · [Next](#newsitem-subtext)
232 |
233 | ---
234 |
235 | NewsItem Subtext
236 | ---
237 | 1. Update the JS. Note: We are factoring out the title part into its own method.
238 | ```javascript
239 | // ...
240 | var moment = require('moment');
241 |
242 | var NewsItem = React.createClass({
243 | // ...
244 |
245 | getCommentLink: function () {
246 | var commentText = 'discuss';
247 | if (this.props.item.kids && this.props.item.kids.length) {
248 | // This only counts top-level comments.
249 | // To get the full count, recursively get item details for this news item.
250 | commentText = this.props.item.kids.length + ' comments';
251 | }
252 |
253 | return (
254 | {commentText}
255 | );
256 | },
257 |
258 | getSubtext: function () {
259 | return (
260 |
303 |
304 | [Previous](#newsitem-domain) · [Next](#newsitem-rank-and-vote)
305 |
306 | ---
307 |
308 | NewsItem Rank and Vote
309 | ---
310 | 1. Update the JS.
311 | ```javascript
312 | var NewsItem = React.createClass({
313 | // ...
314 |
315 | getRank: function () {
316 | return (
317 |
374 |
375 | You have now implemented an HN news item in React.
376 |
377 |
378 |
379 | [Previous](#newsitem-subtext) · [Next](#newsheader)
380 |
381 | ---
382 |
383 | NewsHeader
384 | ---
385 | 1. [Display the logo and title.](#newsheader-logo-and-title)
386 |
387 |
388 |
389 | 1. [Add the nav links.](#newsheader-nav)
390 |
391 |
392 |
393 | 1. [Add the login link.](#newsheader-login)
394 |
395 |
396 |
397 | [Previous](#newsitem-rank-and-vote) · [Next](#newsheader-logo-and-title)
398 |
399 | ---
400 |
401 | NewsHeader Logo and Title
402 | ---
403 | 1. Create a new JS file: /js/NewsHeader.js.
404 | ```javascript
405 | var $ = require('jquery');
406 | var React = require('react');
407 |
408 | var NewsHeader = React.createClass({
409 | getLogo: function () {
410 | return (
411 |
414 | );
415 | },
416 |
417 | getTitle: function () {
418 | return (
419 |
505 |
506 | [Previous](#newsheader) · [Next](#newsheader-nav)
507 |
508 | ---
509 |
510 | NewsHeader Nav
511 | ---
512 | 1. Update the JS.
513 | ```javascript
514 | // ...
515 | var _ = require('lodash');
516 |
517 | var NewsHeader = React.createClass({
518 | // ...
519 |
520 | getNav: function () {
521 | var navLinks = [
522 | {
523 | name: 'new',
524 | url: 'newest'
525 | },
526 | {
527 | name: 'comments',
528 | url: 'newcomments'
529 | },
530 | {
531 | name: 'show',
532 | url: 'show'
533 | },
534 | {
535 | name: 'ask',
536 | url: 'ask'
537 | },
538 | {
539 | name: 'jobs',
540 | url: 'jobs'
541 | },
542 | {
543 | name: 'submit',
544 | url: 'submit'
545 | }
546 | ];
547 |
548 | return (
549 |
586 |
587 | [Previous](#newsheader-logo-and-title) · [Next](#newsheader-login)
588 |
589 | ---
590 |
591 | NewsHeader Login
592 | ---
593 | 1. Update the JS.
594 | ```javascript
595 | var NewsHeader = React.createClass({
596 | // ...
597 |
598 | getLogin: function () {
599 | return (
600 |
626 |
627 | You have now implemented the HN header in React.
628 |
629 |
630 |
631 | [Previous](#newsheader-nav) · [Next](#newslist)
632 |
633 | ---
634 |
635 | NewsList
636 | ---
637 | 1. [Display the header and items.](#newslist-header-and-items)
638 |
639 |
640 |
641 | 1. [Add the more link.](#newslist-more)
642 |
643 |
644 |
645 | [Previous](#newsheader-login) · [Next](#newslist-header-and-items)
646 |
647 | ---
648 |
649 | NewsList Header and Items
650 | ---
651 | 1. Create a new JS file: /js/NewsList.js.
652 | ```javascript
653 | var _ = require('lodash');
654 | var NewsHeader = require('./NewsHeader');
655 | var NewsItem = require('./NewsItem');
656 | var React = require('react');
657 |
658 | var NewsList = React.createClass({
659 | render: function () {
660 | return (
661 |
731 |
732 | [Previous](#newslist) · [Next](#newslist-more)
733 |
734 | ---
735 |
736 | NewsList More
737 | ---
738 | 1. Update the JS.
739 | ```javascript
740 | var NewsList = React.createClass({
741 | // ...
742 |
743 | getMore: function () {
744 | return (
745 |
779 |
780 | You have now implemented the HN item list in React.
781 |
782 |
783 |
784 | [Previous](#newslist-header-and-items) · [Next](#hacker-news-api)
785 |
786 | ---
787 |
788 | Hacker News API
789 | ---
790 | 1. Create a new JS file: /js/app.js.
791 | ```javascript
792 | var _ = require('lodash');
793 | var $ = require('jquery');
794 | var NewsList = require('./NewsList');
795 | var React = require('react');
796 |
797 | // Get the top item ids
798 | $.ajax({
799 | url: 'https://hacker-news.firebaseio.com/v0/topstories.json',
800 | dataType: 'json'
801 | }).then(function (stories) {
802 | // Get the item details in parallel
803 | var detailDeferreds = _(stories.slice(0, 30)).map(function (itemId) {
804 | return $.ajax({
805 | url: 'https://hacker-news.firebaseio.com/v0/item/' + itemId + '.json',
806 | dataType: 'json'
807 | });
808 | }).value();
809 | return $.when.apply($, detailDeferreds);
810 | }).then(function () {
811 | // Extract the response JSON
812 | var items = _(arguments).map(function (argument) {
813 | return argument[0];
814 | }).value();
815 |
816 | // Render the items
817 | React.render(