使用React並作一個簡單的to-do-list

1. 前言

說到React,我從一年以前就開始試着瞭解而且看了相關的入門教程,並且還買過一本《React:引領將來的用戶界面開發框架 》拜讀。React的輕量組件化的思想及其virtual-dom的這種技術創新,也算是早就有了初步瞭解。一來沒有學的太深刻,二來後來在工做中和業餘項目中都沒有用到,所以慢慢的就更加生疏了。javascript

近期,由於我想把本身的開源項目wangEditor能放在React、angular和vuejs中使用。先從react開始,順手本身也重試一下React的基礎知識,順便再作一個小demo,體驗一下React寫程序的一些提倡的思路。通過幾天的回顧學習,我也寫了一個在React中集成wangEditor的簡單demo:http://www.kancloud.cn/wangfupeng/wangeditor2/129376php

不得不說一下,React風靡世界、功能強大、適應性強,可是入門起來至關簡單。反觀angularjs,學習成本就比較高,我還沒弄明白1.x呢,2.0已經出來了。縱然我很是努力,可是某些方面仍是擺脫不了out的命運(見笑...)。css

2. 基礎入門

想入門React,首先你得有比較紮實的javascript語法基礎以及前端開發的基礎知識,不然我下面推薦的教程講的再好,你也咂摸不出啥滋味來。因此若是你是初學者,不要被如今前端這些琳琅滿目的框架、庫迷惑了眼睛,覺得學會了這個那個就好了——基礎不行學啥都白搭。html

閒話很少扯。前人栽樹後人乘涼,給你們推薦兩個我看過的很是好的React入門教程,一個文字一個視頻。前端

這兩篇教程的篇幅都不長 ,閱讀加練習的話,兩個晚上(正常下班吃完飯以後的剩餘時間)絕對能搞定的。固然你若是具有程序員優質的熬夜技能,一夜搞定也說不定啊,創造奇蹟的同時照顧一下身體,哈哈。看完這兩篇教程,你能基本瞭解react的設計思想,技術特色,使用的語法和技巧。作個hello word什麼的,徹底沒啥問題的。vue

 

3. 入門以後

記得幾年前上大學乃至剛畢業那會兒,不管是學java仍是php仍是.net的,會了語法、會寫個hello world確定不能算是入門的。當時跟hello world齊名還有一個東西叫作『留言板』。師生之間常常有這樣的對話。java

  • 老師:xxx技術會用了嗎?
  • 學生:會了
  • 老師:那寫個留言板系統吧,能留言、查看、刪除、回覆
  • 學生:不會
  • 老師:....

上述的『留言板』也隨着幾年以前流行的bbs、論壇、校內網等沒幾年就河了西了(《大宅門》學的),目前用來作demo的通常都是todolist,例如backbone的官方demo就是一個todolist。react

不管是『留言板』仍是『todolist』,咱們須要用它來表達的就是——咱們如何經過這項技術去實現基本的『增刪改查』 這種能力,由於一個系統其餘全部的業務邏輯操做,都是『增刪改查』這幾個功能的拼接。因此,咱們在剛剛接觸一個新東西的時候,就用它來作一個簡單的todolist吧。git

 

4. todolist

作出來大約是這樣子的,很簡單很醜,too 羊 too 森破 sometime native 。不過不要緊,雖然它很醜,可是很溫柔啊。咱們只是拋開了其餘內容,專一於這項技術實現的自己而已。若是你想漂亮一點,本身寫一個css樣式嘍。程序員

下面我將一步一步講解如何使用React來製做出一個簡單的todolist,不過仍是須要你耐心把文章讀完,我也儘可能寫的可讀性強一些,不至於太乏味。

4.1 總體分析

React最大的賣點是輕量組件化。咱們分析一下以上截圖中的頁面,若是要分組件的話,咱們大約能夠分紅一個總組件和兩個子組件。一個輸入內容的組件,一個顯示內容列表(帶刪除功能)的組件,外面再用一個總組件將兩個子組件包括起來。

所以,咱們的代碼的總體結構大約是這麼寫的:

 1 // TodoList 組件是一個總體的組件,最終的React渲染也將只渲染這一個組件
 2 // 該組件用於將『新增』和『列表』兩個組件集成起來
 3 var TodoList = React.createClass({
 4     render: function () {
 5         return (
 6             <div>
 7                 <TypeNew  />
 8                 <ListTodo />
 9             </div>
10         );
11     }
12 });
13 
14 // TypeNew 組件用於新增數據,
15 var TypeNew = React.createClass({
16     render: function () {
17         return (
18             <form>
19                 <input type="text" placeholder="typing a newthing todo" autoComplete="off" />
20             </form>
21         );
22     }
23 });
24 
25 // ListTodo 組件用於展現列表,並能夠刪除某一項內容,
26 var ListTodo = React.createClass({
27     render: function () {
28         return (
29             <ul id="todo-list">
30                 {/* 其中顯示數據列表 */}
31             </ul>
32         );
33     }
34 });
35 
36 // 將 TodoList 組件渲染到頁面
37 React.render(<TodoList />, document.getElementById('container'));

 

4.2. 顯示數據

下面,咱們要把todolist的數據,顯示到列表中,而且每一個數據項後面都顯示一個『刪除』按鈕,就像這樣:

既然是展現數據,首先要考慮數據存儲在哪裏,來自於哪裏。如今這裏放一句話——React提倡全部的數據都是由父組件來管理,經過props的形式傳遞給子組件來處理——先記住,接下來再解釋這句話。

上文提到,作一個todolist頁面須要一個父組件,兩個子組件。父組件固然就是todolist的『總指揮』,兩個子組件分別用來add和show、delete。用通俗的方式講來,父組件就是領導,兩個子組件就是協助領導開展工做的,一切的資源和調動資源的權利,都在領導層級,子組件配合領導工做,須要資源或者調動資源,只能申請領導的批准

這麼說來就明白了吧。數據徹底由父組件來管理和控制,子組件用來顯示、操做數據,得通過父組件的批准,即——父組件經過props的形式將數據傳遞給子組件,子組件拿到父組件傳遞過來的數據,再進行展現。

另外,根據React開發的規範,組件內部的數據由state控制,外部對內部傳遞數據時使用 props 。這麼看來,針對父組件來講,要存儲todolist的數據,那就是內部信息(自己就是本身可控的資源,而不是『領導』控制的資源),用state來存儲便可。而父組件要將todolist數據傳遞給子組件,對子組件來講,那就是傳遞進來的外部信息(是『領導』的資源,交付給你來處理),須要使用props。

好了,咱們再修改一下代碼,用代碼表述一下這個問題:

 1 // TodoList 組件是一個總體的組件,最終的React渲染也將只渲染這一個組件
 2 // 該組件用於將『新增』和『列表』兩個組件集成起來
 3 var TodoList = React.createClass({
 4     // 初始化數據,todolist的數據由state來控制
 5     getInitialState: function () {
 6         return {
 7             todolist: []
 8         };
 9     },
10     render: function () {
11         return (
12             <div>
13                 <TypeNew  />
14                 {/*
15                     集成 ListTodo 組件
16                     todo - 將todolist的數據傳入到組件,用於組件展現數據
17                 */}
18                 <ListTodo todo={this.state.todolist} />
19             </div>
20         );
21     }
22 });
23 
24 // TypeNew 組件用於新增數據,
25 var TypeNew = React.createClass({
26     // 此處省略 ... 字
27 });
28 
29 // ListTodo 組件用於展現列表,並能夠刪除某一項內容,
30 var ListTodo = React.createClass({
31     render: function () {
32         return (
33             <ul id="todo-list">
34             {
35                 // this.props.todo 獲取父組件傳遞過來的數據
36                 // {/* 遍歷數據 */}
37                 this.props.todo.map(function (item, i) {
38                     return (
39                         <li>
40                             <label>{item}</label>
41                             <button>delete</button>
42                         </li>
43                     );
44                 })
45             }
46             </ul>
47         );
48     }
49 });
50 
51 // 將 TodoList 組件渲染到頁面
52 React.render(<TodoList />, document.getElementById('container'));

 

4.3 新增數據

剛纔都把數據展現講完了,可是想展現一下,目前尚未數據呢,那就新增一個吧。以下圖:

根據剛纔的拐彎抹角、高談闊論、旁徵博引的那幾句話,咱們知道,子組件獲得數據後,就須要將新數據添加到todolist的數據中。而todolist的數據是由父組件來管理的,子組件不能說改就改呀,得申請父組件的容許和贊成呀。所以,咱們須要讓父組件開放一個能夠修改數據的接口,而後將這個接口做爲props傳遞給子組件,讓其能修改數據。

另外,子組件調用父組件的接口對todolist數據進行修改了以後,至關於修改了React對象的state數據,此時就會觸發React的自動更新(就是經過virtual-dom對比,而後更新真實的dom那一套),React會將UI實時隨着數據更新,就不用咱們操心了,這也是React比較強大的地方之一。

所以,代碼將改成:

 1 // TodoList 組件是一個總體的組件,最終的React渲染也將只渲染這一個組件
 2 // 該組件用於將『新增』和『列表』兩個組件集成起來
 3 var TodoList = React.createClass({
 4     // 初始化數據,todolist的數據由state來控制
 5     getInitialState: function () {
 6         return {
 7             todolist: []
 8         };
 9     },
10     // 接收一個傳入的數據,並將它實時更新到組件的 state 中,以便組件根據數據從新render
11     // 只要改變了 state ,react自動執行 reader 計算
12     handleChange: function (rows) {
13         this.setState({
14             todolist: rows
15         });
16     },
17     render: function () {
18         return (
19             <div>
20                 {/* 
21                     集成 TypeNews 組件,傳入兩個屬性 onAdd 和 todo
22                     todo - 將todolist的數據傳入到組件,當新增時,更新todolist數據
23                     onAdd -  將 handleChange 函數傳入到組件,新增時,用它來處理最新的todolist數據
24                 */}
25                 <TypeNew onAdd={this.handleChange} todo={this.state.todolist} />
26                 {/*
27                     集成 ListTodo 組件
28                     todo - 將todolist的數據傳入到組件,用於組件展現數據
29                 */}
30                 <ListTodo todo={this.state.todolist} />
31             </div>
32         );
33     }
34 });
35 
36 // TypeNew 組件用於新增數據,它須要 todo 和 onAdd 兩個屬性,上文已經提到過
37 // 基本邏輯是:當從 input 中獲取數據時,將新數據 push 到todo中,
38 // 而後使用 onAdd 調用 TodoList 的 handleChange 來更新state,而後react自動render
39 var TypeNew = React.createClass({
40     handleAdd: function (e) {
41         e.preventDefault();
42         // 經過 refs 獲取dom元素,而後獲取輸入的內容
43         var inputDom = this.refs.inputnew.getDOMNode();
44         var newthing = inputDom.value.trim();
45         // 獲取傳入的todolist數據
46         var rows = this.props.todo;
47         if (newthing !== '') {
48             // 更新數據,並使用 onAdd 更新到 TodoList 組件的 state 中
49             rows.push(newthing);
50             this.props.onAdd(rows);
51         }
52         inputDom.value = '';
53     },
54     render: function () {
55         return (
56             // form submit 時,觸發 handleAdd 事件
57             <form onSubmit={this.handleAdd}>
58                 <input type="text" ref="inputnew" id="todo-new" placeholder="typing a newthing todo" autoComplete="off" />
59             </form>
60         );
61     }
62 });
63 
64 // ListTodo 組件用於展現列表,並能夠刪除某一項內容,
65 var ListTodo = React.createClass({
66     render: function () {
67         return (
68             <ul id="todo-list">
69             {
70                 // this.props.todo 獲取父組件傳遞過來的數據
71                 // {/* 遍歷數據 */}
72                 this.props.todo.map(function (item, i) {
73                     return (
74                         <li>
75                             <label>{item}</label>
76                             <button>delete</button>
77                         </li>
78                     );
79                 }) 
80             }
81             </ul>
82         );
83     }
84 });
85 
86 // 將 TodoList 組件渲染到頁面
87 React.render(<TodoList />, document.getElementById('container'));

 

4.4 刪除數據

刪除數據和新增數據,邏輯上是同樣的,都是須要父組件提供一個修改數據的接口,經過props形式傳遞給子組件,而後讓子組件來調用。就再也不贅述了,直接上代碼,注意看註釋:

 1     // TodoList 組件是一個總體的組件,最終的React渲染也將只渲染這一個組件
 2     // 該組件用於將『新增』和『列表』兩個組件集成起來,而且存儲 todolist 的數據
 3     var TodoList = React.createClass({
 4         // 初始化數據
 5         getInitialState: function () {
 6             return {
 7                 todolist: []
 8             };
 9         },
10         // 接收一個傳入的數據,並將它實時更新到組件的 state 中,以便組件根據數據從新render
11         // 只要改變了 state ,react自動執行 reader 計算
12         handleChange: function (rows) {
13             this.setState({
14                 todolist: rows
15             });
16         },
17         render: function () {
18             return (
19                 <div>
20                     {/* 
21                         集成 TypeNews 組件,傳入兩個屬性 onAdd 和 todo
22                         todo - 將todolist的數據傳入到組件,當新增時,更新todolist數據
23                         onAdd -  將 handleChange 函數傳入到組件,新增時,用它來處理最新的todolist數據
24                     */}
25                     <TypeNew onAdd={this.handleChange} todo={this.state.todolist} />
26                     {/*
27                         集成 ListTodo 組件,傳入兩個屬性 onDel 和 todo
28                         todo - 將todolist的數據傳入到組件,當刪除時,更新todolist數據
29                         onDel - 將 handleChange 函數傳入到組件,刪除時,用它來處理最新的todolist數據
30                     */}
31                     <ListTodo onDel={this.handleChange} todo={this.state.todolist} />
32                 </div>
33             );
34         }
35     });
36 
37     // TypeNew 組件用於新增數據,它須要 todo 和 onAdd 兩個屬性,上文已經提到過
38     // 基本邏輯是:當從 input 中獲取數據時,將新數據 push 到todo中,
39     // 而後使用 onAdd 調用 TodoList 的 handleChange 來更新state,而後react自動render
40     var TypeNew = React.createClass({
41         handleAdd: function (e) {
42             e.preventDefault();
43             // 經過 refs 獲取dom元素,而後獲取輸入的內容
44             var inputDom = this.refs.inputnew.getDOMNode();
45             var newthing = inputDom.value.trim();
46             // 獲取傳入的todolist數據
47             var rows = this.props.todo;
48             if (newthing !== '') {
49                 // 更新數據,並使用 onAdd 更新到 TodoList 組件的 state 中
50                 rows.push(newthing);
51                 this.props.onAdd(rows);
52             }
53             inputDom.value = '';
54         },
55         render: function () {
56             return (
57                 // form submit 時,觸發 handleAdd 事件
58                 <form onSubmit={this.handleAdd}>
59                     <input type="text" ref="inputnew" id="todo-new" placeholder="typing a newthing todo" autoComplete="off" />
60                 </form>
61             );
62         }
63     });
64 
65     // ListTodo 組件用於展現列表,並能夠刪除某一項內容,它有 noDel todo 兩個屬性,上文已經提到過
66     // 它的基本邏輯是:遍歷 todo 的內容,生成數據列表和刪除按鈕
67     // 對某一項執行刪除時,想將 todo 中的數據刪除,
68     // 而後經過 onDel 事件調用 TodoList 的 handleChange 來更新state,而後react自動render
69     var ListTodo = React.createClass({
70         handleDel: function (e) {
71             var delIndex = e.target.getAttribute('data-key');
72             // 更新數據,並使用 onDel 更新到 TodoList 的 state 中,以便 React自動render
73             this.props.todo.splice(delIndex, 1);
74             this.props.onDel(this.props.todo);
75         },
76         render: function () {
77             return (
78                 <ul id="todo-list">
79                 {
80                     // {/* 遍歷數據 */}
81                     this.props.todo.map(function (item, i) {
82                         return (
83                             <li>
84                                 <label>{item}</label>
85                                 <button className="destroy" onClick={this.handleDel} data-key={i}>delete</button>
86                             </li>
87                         );
88                     }.bind(this)) // {/* 綁定函數的執行this - 以便 this.handleDel */}
89                 }
90                 </ul>
91             );
92         }
93     });
94 
95     // 將 TodoList 組件渲染到頁面
96     React.render(<TodoList />, document.getElementById('container'));

 

5. 總結

入門React的基本語法和使用比較簡單,可是想要了解它的工做過程和基本的設計思想,仍是須要一點時間的。接下來,在大型系統中使用React確定又須要更多的時間,你可能還會遇到不少坑,等着你去填。

可是不管如今用仍是不用,我們都不能落伍,該學的仍是得掌握一些比較好。你們共勉。

最後,此文章參考了 http://react-china.org/t/todolist/1075 感謝本文做者

-------------------------------------------------------------------------------------------------------------

歡迎關注個人教程:

使用grunt搭建全自動web前端開發環境json2.js源碼解讀視頻

深刻理解javascript原型和閉包系列》《css知多少》《微軟petshop4.0源碼解讀視頻

------------------------------------------------------------------------------------------------------------

wangEditor,輕量化web富文本編輯器

wangEditor-mobile,適用於手機的富文本編輯器

-------------------------------------------------------------------------------------------------------------

相關文章
相關標籤/搜索