上一篇文章「[React速成班]從TodoList範例學習React(2)-透過實作TodoItems學習props」中,我們學到如何使用props來取得元件傳遞的參數值,也實作了TodoItems及TodoItem元件,藉此了解props的應用。接下來我們將透過實作AddTodoForm部分,來學習React中state的使用。
關於state
在之前文章中我們已經知道如何使用props來取得或傳遞每個元件的參數,然後props在React的世界中是不可以被改變的,以上一篇文章中TodoItem的例子來看,假設我們希望更改this.props.key的值,所以寫成這樣
var TodoItem = React.createClass({
render: function(){
this.props.key = this.props.key + 1; // 更改this.props內容將會出錯
return (<li key={this.props.key}>{this.props.children}</li>);
}
})
這時侯再重新整理瀏覽器就會一片空白,若以Chrome的開發人員工具檢測,會看到如下圖的錯誤訊息:
因此若在執行時希望使用可以變更的資料,就必須使用state。在每個元件中我們可以使用this.state取得目前元件各自的state內的值,若需要更動state,則可以使用this.setState()來改變state的內容,當state內容被改變時,元件將會重新呼render方法。而在每個元件的生命週期裡,我們可以實作 getInitialState方法,來設定初始化的state資料。
實作AddTodoForm
有了基本的state概念後,我們來看看如何將state應用AddTodoForm的元件上。
var AddTodoForm = React.createClass({
getInitialState: function(){
return {todoText: ""};
},
handleTodoChange: function(e){
this.setState({todoText: e.target.value});
},
handleAddTodoItem: function(e){
console.log(this.state.todoText);
// 如何將資料新增到TodoItems中?
},
render: function() {
return (
<div>
<input type="text"
value={this.state.todoText}
onChange={this.handleTodoChange}/>
<button
onClick={this.handleAddTodoItem}>Add Todo Item</button>
</div>
);
}
});
首先我們可以看到getInitialState方法裡我們直接回傳了一個包含todoText屬性的物件,這個物件就會變成我們的this.state的內容,所以在render方法中我們的input裡面可以使用{this.state.todoText}取得state的todoText內容。
同時我們定義了input的onChange事件由handleTodoChange處理,而handleTodoChange則會呼叫this.setState來更新目前的state內容。接著在button中的click事件我們會用console.log來記錄目前的state中todoText的資訊。
因此重新整理瀏覽器後,在文字方塊中輸入內容,按下按鈕後,可以在開發人員視窗中看到state被變更的結果。
修改TodoList元件內容
接下來在TodoList中我們也可以改為使用state的方式來儲存要顯示的todo資料項目:
var TodoList = React.createClass({
getInitialState: function(){
return {
todoItems:
[{ id: 1, data: "Item 1" },
{ id: 2, data: "Item 2" }]
};
},
handleAddTodoItem: function(todoText){
var items = this.state.todoItems;
items.push({
id: items.length + 1,
data: todoText
});
this.setState({todoItems: items});
},
render: function() {
return (
<div className="todoList">
<h1>Todo List</h1>
<TodoItems items={this.state.todoItems}/>
<AddTodoForm addItem={this.handleAddTodoItem}/>
</div>
);
}
});
除了以state方式來儲存todo項目以外,我們也增加了AddTodoForm的addItem屬性,並由TodoList的handleAddTodoItem來處理,而handleAddTodoItem方法中我們傳入一個todoText字串,並且把它加入state中,然後再透過this.setState來更新state內容,讓React知道需要重新render這個元件的內容。
看到addItem=xxx這樣的寫法是不是很熟悉?沒錯!就是我們上一篇文章介紹的props的用法,只是這次傳入的不是一個資料值,而是傳入handleAddTodoItem,因此在我們的AddTodoForm中,我們就可以透過this.props.addItem()方法,來呼叫這個更新todo項目的方法,所以我們可以改寫AddTodoForm裡的handleAddTodoItem內容:
handleAddTodoItem: function(e){
// console.log(this.state.todoText);
// 如何將資料新增到TodoItems中?
// 呼叫以props傳進來的addItem
this.props.addItem(this.state.todoText);
},
這樣就算大功告成啦!我們可以立刻重新整理瀏覽器看到結果如下:
單元回顧
今天我們實作了TodoList最後一個部分AddTodoForm,並學習到state的應用以及一些事件的處理。到目前為止算是對React主要功能做了一個概略性的介紹。
在之後的文章,再來慢慢介紹一些React進階的知識吧!