React Native 每日一学(Learn a little every day)
本文出自《React Native學習筆記》系列文章。
每天一個知識點(技巧,經驗,填坑日記等),每天學一點,離大神近一點。
匯聚知識,分享精華。
如果你是一名React Native愛好者,或者有一顆熱愛鉆研新技術的心,喜歡分享技術干貨、項目經驗、以及你在React Naive學習研究或實踐中的一些經驗心得等等,歡迎投稿《React Native 每日一學》欄目。
如果你是一名Android、iOS、或前端開發人員,有者一顆積極進取的心,歡迎關注《React Native 每日一學》。本欄目匯聚React Native開發的技巧,知識點,經驗等。
列表
D11:AsyncStorage存儲key管理小技巧
場景
AsyncStorage是React Native推薦的數據存儲方式。當我們需要根據條件從本地查詢出多條記錄時,你會想到來一個select * from xx where xx。但是很不幸的告訴你,AsyncStorage
是不支持sql的,因為AsyncStorage是Key-Value存儲系統。
那么如何才能快速的從眾多記錄中將符合條件的記錄查詢出來呢?
請往下看…
AsyncStorage key管理
為了方便查詢多條符合規則的記錄,我們可以在保存數據前,對這條數據進行分類,然后記錄下這條記錄的key。下次再查詢該數據前,只需要先查詢之前保存的key,然后通過
static multiGet(keys, callback?)API,將符合規則的數據一并查詢出來。
用例
保存數據
第一步:保存數據
saveFavoriteItem(key,vaule,callback) {AsyncStorage.setItem(key,vaule,(error,result)=>{if (!error) {//更新Favorite的keythis.updateFavoriteKeys(key,true);}});}第二步:更新key
/*** 更新Favorite key集合* @param isAdd true 添加,false 刪除* **/updateFavoriteKeys(key,isAdd){AsyncStorage.getItem(this.favoriteKey,(error,result)=>{if (!error) {var favoriteKeys=[];if (result) {favoriteKeys=JSON.parse(result);}var index=favoriteKeys.indexOf(key);if(isAdd){if (index===-1)favoriteKeys.push(key);}else {if (index!==-1)favoriteKeys.splice(index, 1);}AsyncStorage.setItem(this.favoriteKey,JSON.stringify(favoriteKeys));}});}查詢批量數據
第一步:查詢key
getFavoriteKeys(){//獲取收藏的Respository對應的keyreturn new Promise((resolve,reject)=>{AsyncStorage.getItem(this.favoriteKey,(error,result)=>{if (!error) {try {resolve(JSON.parse(result));} catch (e) {reject(error);}}else {reject(error);}});});}第二步:根據key查詢數據
AsyncStorage.multiGet(keys, (err, stores) => {try {stores.map((result, i, store) => {// get at each store's key/value so you can work with itlet key = store[i][0];let value = store[i][1];if (value)items.push(JSON.parse(value));});resolve(items);} catch (e) {reject(e);}});```>**以上是我在使用AsyncStorage進行批量數據查詢的一些思路,大家根據實際情況進行調整。** D10:優化切換動畫卡頓的問題(2016-8-31) ------ 使用API InteractionManager,它的作用就是可以使本來JS的一些操作在動畫完成之后執行,這樣就可確保動畫的流程性。當然這是在延遲執行為代價上來獲得幀數的提高。 ```javascriptInteractionManager.runAfterInteractions(()=>{//...耗時較長的同步任務...//更新state也需要時間this.setState({...})//獲取某些數據,需要長時間等待this.fetchData(arguements)})<div class="se-preview-section-delimiter"></div>D9:動態屬性名&字符串模板(2016-8-30)
在 ES6+ 中,我們不僅可以在對象字面量屬性的定義中使用表達式,還有使用使用字符串模板:
class Form extends React.Component {onChange(inputName, e) {this.setState({[`${inputName}Value`]: e.target.value,});} }D8:React-Native 原生模塊調用(iOS) (2016-8-29)
在項目中遇到地圖,撥打電話,清除緩存等iOS與Andiorid機制不同的功能,就需要調用原生的界面或模塊,這里說下React Native調用iOS原生模塊,Andiorid也是大同小異
1.創建原生模塊,實現“RCTBridgeModule”協議
<div class="se-preview-section-delimiter"></div>#import <Foundation/Foundation.h><div class="se-preview-section-delimiter"></div>#import "RCTBridgeModule.h"@interface KSDMapManager : NSObject <RCTBridgeModule>@end<div class="se-preview-section-delimiter"></div>2 導出模塊,導出方法
@implementation KSDMapManager //導出模塊 RCT_EXPORT_MODULE();RCT_EXPORT_METHOD(gotoIM:(RCTResponseSenderBlock)callback) {__weak typeof(self) weakSelf = self;self.callback = callback;UIViewController *controller = (UIViewController*)[[[UIApplication sharedApplication] keyWindow] rootViewController];KSDMapLocationViewController *mapVc = [[KSDMapLocationViewController alloc] init];mapVc.handle = ^(NSString *address) {weakSelf.itemValue = address;NSArray *events = [[NSArray alloc] initWithObjects:self.itemValue, nil];callback(events);}; [controller presentViewController:mapVc animated:YES completion:nil]; }<div class="se-preview-section-delimiter"></div>3 js文件中調用
//創建原生模塊實例 var KSDMapManager = NativeModules.KSDMapManager; //方法調用 KSDMapManager.gotoIM((events)=>{this._inputReceiveAddress(events);console.log(events);}) <div class="se-preview-section-delimiter"></div>D7:解構賦值(Destructuring assignment)(2016-8-26)
解構賦值語法是JavaScript的一種表達式,可以方便的從數組或者對象中快速提取值賦給定義的變量。
獲取數組中的值
從數組中獲取值并賦值到變量中,變量的順序與數組中對象順序對應。
var foo = ["one", "two", "three", "four"];var [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three"//如果你要忽略某些值,你可以按照下面的寫法獲取你想要的值 var [first, , , last] = foo; console.log(first); // "one" console.log(last); // "four"//你也可以這樣寫 var a, b; //先聲明變量[a, b] = [1, 2]; console.log(a); // 1 console.log(b); // 2<div class="se-preview-section-delimiter"></div>如果沒有從數組中的獲取到值,你可以為變量設置一個默認值。
var a, b;[a=5, b=7] = [1]; console.log(a); // 1 console.log(b); // 7<div class="se-preview-section-delimiter"></div>通過解構賦值可以方便的交換兩個變量的值。
var a = 1; var b = 3;[a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1<div class="se-preview-section-delimiter"></div>獲取對象的值
從對象中獲取對象屬性的值,在聲明變量的時候要與對象的屬性名保持一致。
var o = {p: 42, q: true}; var {p, q} = o;console.log(p); // 42 console.log(q); // true//你也可以這樣寫 var a, b;({a, b} = {a:1, b:2});console.log(a); // 1 console.log(b); // 2<div class="se-preview-section-delimiter"></div>可以從一個對象中獲取對象屬性的值并賦值給與對象屬性名不同的變量。
var o = {p: 42, q: true}; var {p: foo, q: bar} = o;console.log(foo); // 42 console.log(bar); // true <div class="se-preview-section-delimiter"></div>和獲取數組中的值一樣,從對象中獲取屬性的值也可以設置一個默認值。
var {a=10, b=5} = {a: 3};console.log(a); // 3 console.log(b); // 5<div class="se-preview-section-delimiter"></div>D6:ref屬性不只是string(2016-8-25)
ref屬性不僅接受string類型的參數,而且它還接受一個function作為callback。這一特性讓開發者對ref的使用更加靈活。
render: function() {return (<TextInput ref={function(input) {if (input != null) {input.focus();}}} />);},<div class="se-preview-section-delimiter"></div>在ES6中我們可以使用箭頭函數來為組件的ref設置一個callback。
render() {return <TextInput ref={(c) => this._input = c} />;},componentDidMount() {this._input.focus();},<div class="se-preview-section-delimiter"></div>需要提醒大家的是,只有在組件的render方法被調用時,ref才會被調用,組件才會返回ref。如果你在調用this.refs.xx時render方法還沒被調用,那么你得到的是undefined。
心得:ref屬性在開發中使用頻率很高,使用它你可以獲取到任何你想要獲取的組件的對象,有個這個對象你就可以靈活地做很多事情,比如:讀寫對象的變量,甚至調用對象的函數。
D5:React Native setNativeProps使用(2016-8-24)
有時候我們需要直接改動組件并觸發局部的刷新,但不使用state或是props。
setNativeProps 方法可以理解為web的直接修改dom。使用該方法修改 View 、 Text 等 RN自帶的組件 ,則不會觸發組件的 componentWillReceiveProps 、 shouldComponentUpdate 、componentWillUpdate 等組件生命周期中的方法。
使用例子
javascript
class MyButton extends React.Component({
setNativeProps(nativeProps) {
this._root.setNativeProps({ //這里輸入你要修改的組件style
height:48,
backgroundColor:'red'
});
},
render() {
return (
<View ref={component => this._root = component} {...this.props} style={styles.button}>
<Text>{this.props.label}</Text>
</View>
)
},
});
避免和render方法的沖突
如果要更新一個由render方法來維護的屬性,則可能會碰到一些出人意料的bug。因為每一次組件重新渲染都可能引起屬性變化,這樣一來,之前通過setNativeProps所設定的值就被完全忽略和覆蓋掉了。
D4:React Native 函數的綁定 (2016-8-23)
在ES6的class中函數不再被自動綁定,你需要手動去綁定它們。
第一種在構造函數里綁定。
constructor(props) {super(props); // Set up initial statethis.state = {text: props.initialValue || 'placeholder' };// Functions must be bound manually with ES6 classesthis.handleChange = this.handleChange.bind(this);另一種方式就是在你使用的地方通過內聯來綁定:
// Use `.bind`:render() { return (<input onChange={this.handleChange.bind(this)}value={this.state.text} /> ); } // Use an arrow function: render() {return (<input onChange={() => this.handleChange()} value={this.state.text} /> );以上任意一種都可以,然而在效率上卻不行了。每一次調用render(可以說是非常頻繁!)一個新的函數都會被創建。與在構造函數里只綁定一次相比就慢一些。
最終的選擇是使用箭頭函數直接替換函數在類中的聲明,像這樣:
// the normal way // requires binding elsewhere handleChange(event) {this.setState({text: event.target.value}); } // the ES7 way // all done, no binding required handleChange = (event) => { this.setState({ text: event.target.value}); }通過這種方式,你不需要綁定任何東西。這都已經通過神奇的箭頭函數被搞定了。像期望的那樣,函數內部的this將會指向組件實例。
參考:http://www.jianshu.com/p/a4c23654932e
D3:React Native 真機調試 (2016-8-22)
開發中真機調試是必不可少的,有些功能和問題模擬器是無法重現的,所以就需要配合真機測試,接下來就說下安卓和iOS的真機調試,不難,但是有很多細節需要注意
iOS 真機調試
Android 真機調試
在 Android 設備上打開 USB debugging 并連接上電腦啟動調試。
在真機上運行的方法與在模擬器上運行一致,都是通過 react-native run-android 來安裝并且運行你的 React Native 應用。
如果不是 Android 5.0+ (API 21) ,那么就沒辦法通過 adb reverse 進行調試,需要通過 WiFi 來連接上你的開發者服務器
注:因為本人不是安卓開發,所以參考http://my.oschina.net/imot/blog/512808
細節
其實還是有些坑的,這里只說iOS 如開始所說,必須是同一網絡下,有時電腦同時開著Wifi和插著網線,建議把網線拔掉,但是也不排除可以,沒有試過,還有就是
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
這一句千萬不能注釋,需要注意的就這幾點,很簡單
D2:React Native import 文件的小技巧 (2016-8-19)
開發中經常需要 import 其他 js 文件,如果需要同時導入一些相關的 js 文件時,可以創建一個索引文件方便引用。
第一步:創建index.js
在 index.js 中 import 相關的 js 文件
'use strict';import * as Type from './network/EnvironmentConst'; import Request from './network/RequestManager'; import AppContext from './network/AppContext'; import ApiServiceFactory from './network/ApiServiceFactory';module.exports = {ApiServiceFactory,Type,Request,AppContext };<div class="se-preview-section-delimiter"></div>第二步:使用
如果需要使用這些類,只需要導入index文件就可以了~
import {Request, ApiServiceFactory, AppContext, Type} from '../expand/index';<div class="se-preview-section-delimiter"></div>D1:React Native 讀取本地的json文件 (2016-8-18)
自 React Native 0.4.3,你可以以導入的形式,來讀取本地的json文件,導入的文件可以作為一個js對象使用。
第一步:導入json文件
var langsData = require('../../../res/data/langs.json');<div class="se-preview-section-delimiter"></div>ES6/ES2015
import langsData from '../../../res/data/langs.json'<div class="se-preview-section-delimiter"></div>第二步:使用
如果langs.json的路徑正確切沒有格式錯誤,那么現在你可以操作langsData對象了。
Usage
讀取langs.json
使用langs.json
@How to fetch data from local JSON file on react native?
React Native 每日一學(Learn a little every day)
匯聚知識,分享精華。
如果你是一名React Native愛好者,或者有一顆熱愛鉆研新技術的心,喜歡分享技術干貨、項目經驗、以及你在React Naive學習研究或實踐中的一些經驗心得等等,歡迎投稿《React Native 每日一學》欄目。
如果你是一名Android、iOS、或前端開發人員,有者一顆積極進取的心,歡迎關注《React Native 每日一學》。本欄目匯聚React Native開發的技巧,知識點,經驗等。
列表
D8:React-Native 原生模塊調用(iOS) (2016-8-29)
在項目中遇到地圖,撥打電話,清除緩存等iOS與Andiorid機制不同的功能,就需要調用原生的界面或模塊,這里說下React Native調用iOS原生模塊,Andiorid也是大同小異
1.創建原生模塊,實現“RCTBridgeModule”協議
<div class="se-preview-section-delimiter"></div>#import <Foundation/Foundation.h><div class="se-preview-section-delimiter"></div>#import "RCTBridgeModule.h"@interface KSDMapManager : NSObject <RCTBridgeModule>@end<div class="se-preview-section-delimiter"></div>2 導出模塊,導出方法
@implementation KSDMapManager //導出模塊 RCT_EXPORT_MODULE();RCT_EXPORT_METHOD(gotoIM:(RCTResponseSenderBlock)callback) {__weak typeof(self) weakSelf = self;self.callback = callback;UIViewController *controller = (UIViewController*)[[[UIApplication sharedApplication] keyWindow] rootViewController];KSDMapLocationViewController *mapVc = [[KSDMapLocationViewController alloc] init];mapVc.handle = ^(NSString *address) {weakSelf.itemValue = address;NSArray *events = [[NSArray alloc] initWithObjects:self.itemValue, nil];callback(events);}; [controller presentViewController:mapVc animated:YES completion:nil]; }<div class="se-preview-section-delimiter"></div>3 js文件中調用
//創建原生模塊實例 var KSDMapManager = NativeModules.KSDMapManager; //方法調用 KSDMapManager.gotoIM((events)=>{this._inputReceiveAddress(events);console.log(events);}) <div class="se-preview-section-delimiter"></div>D7:解構賦值(Destructuring assignment)(2016-8-26)
解構賦值語法是JavaScript的一種表達式,可以方便的從數組或者對象中快速提取值賦給定義的變量。
獲取數組中的值
從數組中獲取值并賦值到變量中,變量的順序與數組中對象順序對應。
var foo = ["one", "two", "three", "four"];var [one, two, three] = foo; console.log(one); // "one" console.log(two); // "two" console.log(three); // "three"//如果你要忽略某些值,你可以按照下面的寫法獲取你想要的值 var [first, , , last] = foo; console.log(first); // "one" console.log(last); // "four"//你也可以這樣寫 var a, b; //先聲明變量[a, b] = [1, 2]; console.log(a); // 1 console.log(b); // 2<div class="se-preview-section-delimiter"></div>如果沒有從數組中的獲取到值,你可以為變量設置一個默認值。
var a, b;[a=5, b=7] = [1]; console.log(a); // 1 console.log(b); // 7<div class="se-preview-section-delimiter"></div>通過解構賦值可以方便的交換兩個變量的值。
var a = 1; var b = 3;[a, b] = [b, a]; console.log(a); // 3 console.log(b); // 1<div class="se-preview-section-delimiter"></div>獲取對象的值
從對象中獲取對象屬性的值,在聲明變量的時候要與對象的屬性名保持一致。
var o = {p: 42, q: true}; var {p, q} = o;console.log(p); // 42 console.log(q); // true//你也可以這樣寫 var a, b;({a, b} = {a:1, b:2});console.log(a); // 1 console.log(b); // 2<div class="se-preview-section-delimiter"></div>可以從一個對象中獲取對象屬性的值并賦值給與對象屬性名不同的變量。
var o = {p: 42, q: true}; var {p: foo, q: bar} = o;console.log(foo); // 42 console.log(bar); // true <div class="se-preview-section-delimiter"></div>和獲取數組中的值一樣,從對象中獲取屬性的值也可以設置一個默認值。
var {a=10, b=5} = {a: 3};console.log(a); // 3 console.log(b); // 5<div class="se-preview-section-delimiter"></div>D6:ref屬性不只是string(2016-8-25)
ref屬性不僅接受string類型的參數,而且它還接受一個function作為callback。這一特性讓開發者對ref的使用更加靈活。
render: function() {return (<TextInput ref={function(input) {if (input != null) {input.focus();}}} />);},<div class="se-preview-section-delimiter"></div>在ES6中我們可以使用箭頭函數來為組件的ref設置一個callback。
render() {return <TextInput ref={(c) => this._input = c} />;},componentDidMount() {this._input.focus();},<div class="se-preview-section-delimiter"></div>需要提醒大家的是,只有在組件的render方法被調用時,ref才會被調用,組件才會返回ref。如果你在調用this.refs.xx時render方法還沒被調用,那么你得到的是undefined。
心得:ref屬性在開發中使用頻率很高,使用它你可以獲取到任何你想要獲取的組件的對象,有個這個對象你就可以靈活地做很多事情,比如:讀寫對象的變量,甚至調用對象的函數。
D5:React Native setNativeProps使用(2016-8-24)
有時候我們需要直接改動組件并觸發局部的刷新,但不使用state或是props。
setNativeProps 方法可以理解為web的直接修改dom。使用該方法修改 View 、 Text 等 RN自帶的組件 ,則不會觸發組件的 componentWillReceiveProps 、 shouldComponentUpdate 、componentWillUpdate 等組件生命周期中的方法。
使用例子
javascript
class MyButton extends React.Component({
setNativeProps(nativeProps) {
this._root.setNativeProps({ //這里輸入你要修改的組件style
height:48,
backgroundColor:'red'
});
},
render() {
return (
<View ref={component => this._root = component} {...this.props} style={styles.button}>
<Text>{this.props.label}</Text>
</View>
)
},
});
避免和render方法的沖突
如果要更新一個由render方法來維護的屬性,則可能會碰到一些出人意料的bug。因為每一次組件重新渲染都可能引起屬性變化,這樣一來,之前通過setNativeProps所設定的值就被完全忽略和覆蓋掉了。
D4:React Native 函數的綁定 (2016-8-23)
在ES6的class中函數不再被自動綁定,你需要手動去綁定它們。
第一種在構造函數里綁定。
constructor(props) {super(props); // Set up initial statethis.state = {text: props.initialValue || 'placeholder' };// Functions must be bound manually with ES6 classesthis.handleChange = this.handleChange.bind(this);另一種方式就是在你使用的地方通過內聯來綁定:
// Use `.bind`:render() { return (<input onChange={this.handleChange.bind(this)}value={this.state.text} /> ); } // Use an arrow function: render() {return (<input onChange={() => this.handleChange()} value={this.state.text} /> );以上任意一種都可以,然而在效率上卻不行了。每一次調用render(可以說是非常頻繁!)一個新的函數都會被創建。與在構造函數里只綁定一次相比就慢一些。
最終的選擇是使用箭頭函數直接替換函數在類中的聲明,像這樣:
// the normal way // requires binding elsewhere handleChange(event) {this.setState({text: event.target.value}); } // the ES7 way // all done, no binding required handleChange = (event) => { this.setState({ text: event.target.value}); }通過這種方式,你不需要綁定任何東西。這都已經通過神奇的箭頭函數被搞定了。像期望的那樣,函數內部的this將會指向組件實例。
參考:http://www.jianshu.com/p/a4c23654932e
D3:React Native 真機調試 (2016-8-22)
開發中真機調試是必不可少的,有些功能和問題模擬器是無法重現的,所以就需要配合真機測試,接下來就說下安卓和iOS的真機調試,不難,但是有很多細節需要注意
iOS 真機調試
Android 真機調試
在 Android 設備上打開 USB debugging 并連接上電腦啟動調試。
在真機上運行的方法與在模擬器上運行一致,都是通過 react-native run-android 來安裝并且運行你的 React Native 應用。
如果不是 Android 5.0+ (API 21) ,那么就沒辦法通過 adb reverse 進行調試,需要通過 WiFi 來連接上你的開發者服務器
注:因為本人不是安卓開發,所以參考http://my.oschina.net/imot/blog/512808
細節
其實還是有些坑的,這里只說iOS 如開始所說,必須是同一網絡下,有時電腦同時開著Wifi和插著網線,建議把網線拔掉,但是也不排除可以,沒有試過,還有就是
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
這一句千萬不能注釋,需要注意的就這幾點,很簡單
D2:React Native import 文件的小技巧 (2016-8-19)
開發中經常需要 import 其他 js 文件,如果需要同時導入一些相關的 js 文件時,可以創建一個索引文件方便引用。
第一步:創建index.js
在 index.js 中 import 相關的 js 文件
'use strict';import * as Type from './network/EnvironmentConst'; import Request from './network/RequestManager'; import AppContext from './network/AppContext'; import ApiServiceFactory from './network/ApiServiceFactory';module.exports = {ApiServiceFactory,Type,Request,AppContext };<div class="se-preview-section-delimiter"></div>第二步:使用
如果需要使用這些類,只需要導入index文件就可以了~
import {Request, ApiServiceFactory, AppContext, Type} from '../expand/index';<div class="se-preview-section-delimiter"></div>D1:React Native 讀取本地的json文件 (2016-8-18)
自 React Native 0.4.3,你可以以導入的形式,來讀取本地的json文件,導入的文件可以作為一個js對象使用。
第一步:導入json文件
var langsData = require('../../../res/data/langs.json');<div class="se-preview-section-delimiter"></div>ES6/ES2015
import langsData from '../../../res/data/langs.json'第二步:使用
如果langs.json的路徑正確切沒有格式錯誤,那么現在你可以操作langsData對象了。
Usage
讀取langs.json
使用langs.json
@How to fetch data from local JSON file on react native?
About
本文出自《React Native學習筆記》系列文章。
了解更多,可以關注我的:
GitHub
微博
http://jiapenghui.com
推薦閱讀
- React Native 學習筆記
- React Native Awesome(匯聚知識,分享精華):匯集了各類react-native學習資料、工具、組件、開源App、資源下載、以及相關新聞等。
總結
以上是生活随笔為你收集整理的React Native 每日一学(Learn a little every day)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HUAWEI华为MateBook X P
- 下一篇: centos添加第二张网卡