React Native開發筆記
Boolean 屬性
省略一個屬性的值會導致JSX把它當做 true。要傳值 false必須使用屬性表達式。這常出現於使用HTML表單元素,含有屬性如disabled, required, checked 和 readOnly。
// 在JSX中,對於禁用按鈕這二者是相同的。
<input type="button" disabled />;
<input type="button" disabled={true} />;
// 在JSX中,對於不禁用按鈕這二者是相同的。
<input type="button" />;
<input type="button" disabled={false} />;
JSX 展開屬性
如果你事先知道組件需要的全部 Props(屬性),JSX 很容易地這樣寫:
var component = <Component foo={x} bar={y} />;
修改 Props 是不好的,明白嗎
如果你不知道要設置哪些 Props,那麼現在最好不要設置它:
var component = <Component />;
component.props.foo = x; // 不好
component.props.bar = y; // 同樣不好
這樣是反模式,因爲 React 不能幫你檢查屬性類型(propTypes)。這樣即使你的 屬性類型有錯誤也不能得到清晰的錯誤提示。
Props 應該被認爲是不可變的。在別處修改 props 對象可能會導致預料之外的結果,所以原則上這將是一個凍結的對象。
展開屬性(Spread Attributes)
現在你可以使用 JSX 的新特性 - 展開屬性:
var props = {};
props.foo = x;
props.bar = y;
var component = <Component {...props} />;
傳入對象的屬性會被複制到組件內。
它能被多次使用,也可以和其它屬性一起用。注意順序很重要,後面的會覆蓋掉前面的。
var props = { foo: 'default' };
var component = <Component {...props} foo={'override'} />;
console.log(component.props.foo); // 'override'
這個奇怪的 … 標記是什麼?
這個 … 操作符(增強的操作符)已經被 ES6 數組 支持。相關的還有 ECMAScript 規範草案中的 Object 剩餘和展開屬性(Rest and Spread Properties)。我們利用了這些還在制定中標準中已經被支持的特性來使 JSX 擁有更優雅的語法。
State
組件其實是狀態機(State Machines)
React 把用戶界面當作簡單狀態機。把用戶界面想像成擁有不同狀態然後渲染這些狀態,可以輕鬆讓用戶界面和數據保持一致。
React 裏,只需更新組件的 state,然後根據新的 state 重新渲染用戶界面(不要操作 DOM)。React 來決定如何最高效地更新 DOM。
State 工作原理
常用的通知 React 數據變化的方法是調用 setState(data, callback)。這個方法會合並(merge) data 到 this.state,並重新渲染組件。渲染完成後,調用可選的 callback 回調。大部分情況下不需要提供 callback,因爲 React 會負責把界面更新到最新狀態。
哪些組件應該有 State?
大部分組件的工作應該是從 props 裏取數據並渲染出來。但是,有時需要對用戶輸入、服務器請求或者時間變化等作出響應,這時才需要使用 State。
嘗試把儘可能多的組件無狀態化。 這樣做能隔離 state,把它放到最合理的地方,也能減少冗餘,同時易於解釋程序運作過程。
常用的模式是創建多個只負責渲染數據的無狀態(stateless)組件,在它們的上層創建一個有狀態(stateful)組件並把它的狀態通過 props 傳給子級。這個有狀態的組件封裝了所有用戶的交互邏輯,而這些無狀態組件則負責聲明式地渲染數據。
哪些 應該 作爲 State?
State 應該包括那些可能被組件的事件處理器改變並觸發用戶界面更新的數據。 真實的應用中這種數據一般都很小且能被 JSON 序列化。當創建一個狀態化的組件時,想象一下表示它的狀態最少需要哪些數據,並只把這些數據存入 this.state。在 render() 裏再根據 state 來計算你需要的其它數據。你會發現以這種方式思考和開發程序最終往往是正確的,因爲如果在 state 裏添加冗餘數據或計算所得數據,需要你經常手動保持數據同步,不能讓 React 來幫你處理。
哪些 不應該 作爲 State?
this.state 應該僅包括能表示用戶界面狀態所需的最少數據。因些,它不應該包括:
計算所得數據: 不要擔心根據 state 來預先計算數據 —— 把所有的計算都放到 render() 裏更容易保證用戶界面和數據的一致性。例如,在 state 裏有一個數組(listItems),我們要把數組長度渲染成字符串, 直接在 render() 裏使用 this.state.listItems.length + ’ list items’ 比把它放到 state 裏好的多。
React 組件: 在 render() 裏使用當前 props 和 state 來創建它。
基於 props 的重複數據: 儘可能使用 props 來作爲實際狀態的源。把 props 保存到 state 的一個有效的場景是需要知道它以前值的時候,因爲 props 可能因爲父組件重繪的結果而變化。
自動綁定(Autobinding)和事件代理(Event Delegation)
在幕後,React 做了一些操作來讓代碼高效運行且易於理解。
Autobinding: 在 JavaScript 裏創建回調的時候,爲了保證 this 的正確性,一般都需要顯式地綁定方法到它的實例上。在 React,所有方法被自動綁定到了它的組件實例上(除非使用ES6的class符號)。React 還緩存這些綁定方法,所以 CPU 和內存都是非常高效。而且還能減少打字!
事件代理 : React 實際並沒有把事件處理器綁定到節點本身。當 React 啓動的時候,它在最外層使用唯一一個事件監聽器處理所有事件。當組件被加載和卸載時,只是在內部映射裏添加或刪除事件處理器。當事件觸發,React 根據映射來決定如何分發。當映射裏處理器時,會當作空操作處理。參考 David Walsh 很棒的文章 瞭解這樣做高效的原因。
傳遞 Props:捷徑
有一些常用的 React 組件只是對 HTML 做簡單擴展。通常,你想 複製任何傳進你的組件的HTML屬性 到底層的HTML元素上。爲了減少輸入,你可以用 JSX spread 語法來完成:
var CheckLink = React.createClass({
render: function() {
// 這樣會把 CheckList 所有的 props 複製到 <a>
return <a {...this.props}>{'√ '}{this.props.children}</a>;
}
});
ReactDOM.render(
<CheckLink href="/checked.html">
Click here!
</CheckLink>,
document.getElementById('example')
);
箭頭函數=>
箭頭函數就是個簡寫形式的函數表達式,並且它擁有詞法作用域的this值(即不會新產生自己作用域下的this, arguments, super 和 new.target 等對象)。此外,箭頭函數總是匿名的。
更短的函數
在一些函數式編程模式裏,更短的函數書寫方式很受歡迎。試比較:
var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];
var a2 = a.map(function(s){ return s.length });
var a3 = a.map( s => s.length );
剩餘參數
如果一個函數的最後一個形參是以 … 爲前綴的,則在函數被調用時,該形參會成爲一個數組,數組中的元素都是傳遞給該函數的多出來的實參的值.
//因爲theArgs是個數組,所以你可以使用length屬性得到剩餘參數的個數:
function fun1(...theArgs) {
alert(theArgs.length);
}
fun1(); // 彈出 "0", 因爲theArgs沒有元素
fun1(5); // 彈出 "1", 因爲theArgs只有一個元素
fun1(5, 6, 7); // 彈出 "3", 因爲theArgs有三個元素
Promises
ES6 對 Promise 有了原生的支持,一個 Promise 是一個等待被異步執行的對象,當它執行完成後,其狀態會變成 resolved 或者rejected。
var p = new Promise(function(resolve, reject) {
if (/* condition */) {
// fulfilled successfully
resolve(/* value */);
} else {
// error, rejected
reject(/* reason */);
}
});
每一個 Promise 都有一個 .then 方法,這個方法接受兩個參數,第一個是處理 resolved 狀態的回調,一個是處理 rejected 狀態的回調:
p.then((val) => console.log("Promise Resolved", val),
(err) => console.log("Promise Rejected", err));
組件的生命週期
組件的生命週期有三個主要部分:
- 掛載: 組件被注入DOM。
- 更新: 組件被重新渲染來決定DOM是否應被更新。
- 卸載: 組件從DOM中被移除。
React提供生命週期方法,以便你可以指定鉤掛到這個過程上。我們提供了 will 方法,該方法在某事發生前被調用,did方法,在某事發生後被調用。
掛載
getInitialState(): object 在組件掛載前被調用. 有狀態組件(Stateful - - components) 應該實現此函數並返回初始state的數據。
componentWillMount() 在掛載發生前被立即調用。
componentDidMount() 在掛載發生後被立即調用。 需要DOM node的初始化應該放在這裏。
更新
componentWillReceiveProps(object nextProps) 當掛載的組件接收到新的props時被調用。此方法應該被用於比較this.props 和 nextProps以用於使用this.setState()執行狀態轉換。
shouldComponentUpdate(object nextProps, object nextState): boolean 當組件決定任何改變是否要更新到DOM時被調用。作爲一個優化實現比較this.props 和 nextProps 、this.state 和 nextState ,如果React應該跳過更新,返回false。
componentWillUpdate(object nextProps, object nextState) 在更新發生前被立即調用。你不能在此調用this.setState()。
componentDidUpdate(object prevProps, object prevState) 在更新發生後被立即調用。
卸載
- componentWillUnmount() 在組件被卸載和摧毀後被立即調用。清理應該放在這裏。