您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“使用React Native/Redux開發中會遇到什么錯誤”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“使用React Native/Redux開發中會遇到什么錯誤”這篇文章吧。
1.錯誤的估計
有可能你對***個React Native(RN)應用程序的預估是完全錯誤的!
1)你需要分別考慮iOS和Android版本的布局!在布局的時候,有很多組件可以重復使用;如果ios和Android的頁面結構不同,就需要對他們分開單獨布局。
2)對form進行評估時,***也考慮一下數據層驗證。
3)了解數據庫結構,有助于正確地規劃redux
2.盡量使用基礎組件(buttons,footers,headers,inputs,texts)
google搜索RN的基礎組件,你會發現有很多現有組件可以方便的用到項目中,如buttons,footers等。如果項目中沒有特別的布局設計,只需要使用這些基礎組件就可以構建一個頁面。如果有一些特殊的設計,例如,特殊的button樣式,你就需要為每個按鈕設置自定義樣式。你可以封裝已經構建好的組件,并為它們定制樣式。但是我覺得使用View,Text,TouchableOpacity和RN的其他組件來構建自己的組件更加有意義。因為你會有更多的rn實踐,并且深刻理解如何使用RN。最重要的一點,你可以確定你自己構建的組件版本不會被改變。
3.不要把iOS和Android的布局分開
如果iOS和Android布局大致一樣,只有一小部分不同,你可以簡單地使用RN提供的Platform API根據設備平臺進行區分。
如果布局完全不同 – ***分散在不同的文件中單獨布局。
如果你給一個文件命名為index.ios.js – 程序打包時,在iOS中將使用這個文件顯示iOS布局。 index.android.js也是一樣的道理。
你可能會問:“代碼怎么復用呢?” 你可以將重復的代碼移動到助手函數中。需要的時候只復用這些助手函數。
4.錯誤的redux store規劃。
初學者經常會犯的一個很大的錯誤就是,當你在規劃你的應用程序時,你可能會考慮很多布局相關的問題,卻很少考慮關于數據的處理。
Redux能夠幫助我們正確地存儲數據。如果redux規劃的好 – 它將是管理應用程序數據的強大工具。
當我剛剛開始構建RN應用程序時,我曾考慮將reducers作為每個container的數據存儲。所以,如果你有登錄,忘記密碼,待辦事項列表頁面 – 使用他們的縮寫比較簡單:SignIn, Forgot, ToDoList.
在進行了一段工作后,我發現管理數據沒有想象中的容易。
當我從ToDo列表中選擇項目時 – 我需要將數據傳遞給ToDoDetails reducer。這意味著使用了額外的操作來發送數據到reducer。
在做了一些調查之后,我決定以不同的方式規劃結構。一個例子:
鴻蒙官方戰略合作共建——HarmonyOS技術社區
Auth
Todos
Friends
Auth用于存儲認證的token。
而ToDos和Friends reducers用于存儲實體,當我去ToDo Detail頁面時 – 我只需要通過ID搜索所有的ToDos。
對于更復雜的結構,我推薦使用這種規劃,你可以快速的定位到你想找到的。
5.錯誤的項目結構
作為初學者時,總是規劃不好項目結構。
首先 ,需要分析你的項目是不是足夠大?
你的應用程序中有多少個頁面? 20?30?10?5?還是只有一個”Hello World”頁面?
我遇到并開始實施的***個結構是這樣的:
如果你的項目不超過10個頁面,使用上面這種結構是沒有問題的。但是如果項目特別大 – 你可以考慮這樣規劃結構:
區別在于,***種類型建議我們將actions和reducers與container分開存儲。第二種- 把它們存儲在一起。如果應用程序很小 – 將redux模塊從container中分離出來會更加有用。
如果你有通用的樣式(如Header、Footer、Buttons) – 你可以單***建一個名為“styles”的文件夾,在那里設置一個index.js文件并在其中寫入通用樣式。然后在每個頁面上重復使用它們。
實際項目中會有很多不同的結構。你應該了解使用哪種結構更適合你的需求。
6.錯誤的container結構。沒有從一開始就使用smart/dumb組件
當你開始使用RN并初始化項目時,index.ios.js文件中已經有一些代碼,存儲在一個單獨的對象中。
在實際開發項目中,你將需要使用很多組件,不僅僅是由RN提供的,還有自己構建的一些組件。構建container時,可以重用它們。
考慮該組件:
import React, { Component } from ‘react’; import { Text, TextInput, View, TouchableOpacity } from ‘react-native’; import styles from ‘./styles.ios’; export default class SomeContainer extends Component { constructor(props){ super(props); this.state = { username:null } } _usernameChanged(event){ this.setState({ username:event.nativeEvent.text }); } _submit(){ if(this.state.username){ console.log(`Hello, ${this.state.username}!`); } else{ console.log(‘Please, enter username’); } } render() { return ( <View style={styles.container}> <View style={styles.avatarBlock}> <Image source={this.props.image} style={styles.avatar}/> </View> <View style={styles.form}> <View style={styles.formItem}> <Text>Username</Text> <TextInput onChange={this._usernameChanged.bind(this)} value={this.state.username} /> </View> </View> <TouchableOpacity onPress={this._submit.bind(this)}> <View style={styles.btn}> <Text style={styles.btnText}> Submit </Text> </View> </TouchableOpacity> </View> ); } }
所有樣式都存儲在一個單獨的模塊中。
包裹在TouchableOpacity中的button組件應該單獨分離出來,這樣才能方便我們以后重復使用它。Image組件,以后也可能重復使用,所以也應該把它分離出來。
做了一些改變之后的樣子:
import React, { Component, PropTypes } from 'react'; import { Text, TextInput, View, TouchableOpacity } from 'react-native'; import styles from './styles.ios'; class Avatar extends Component{ constructor(props){ super(props); } render(){ if(this.props.imgSrc){ return( <View style={styles.avatarBlock}> <Image source={this.props.imgSrc} style={styles.avatar}/> </View> ) } return null; } } Avatar.propTypes = { imgSrc: PropTypes.object } class FormItem extends Component{ constructor(props){ super(props); } render(){ let title = this.props.title; return( <View style={styles.formItem}> <Text> {title} </Text> <TextInput onChange={this.props.onChange} value={this.props.value} /> </View> ) } } FormItem.propTypes = { title: PropTypes.string, value: PropTypes.string, onChange: PropTypes.func.isRequired } class Button extends Component{ constructor(props){ super(props); } render(){ let title = this.props.title; return( <TouchableOpacity onPress={this.props.onPress}> <View style={styles.btn}> <Text style={styles.btnText}> {title} </Text> </View> </TouchableOpacity> ) } } Button.propTypes = { title: PropTypes.string, onPress: PropTypes.func.isRequired } export default class SomeContainer extends Component { constructor(props){ super(props); this.state = { username:null } } _usernameChanged(event){ this.setState({ username:event.nativeEvent.text }); } _submit(){ if(this.state.username){ console.log(`Hello, ${this.state.username}!`); } else{ console.log('Please, enter username'); } } render() { return ( <View style={styles.container}> <Avatar imgSrc={this.props.image} /> <View style={styles.form}> <FormItem title={"Username"} value={this.state.username} onChange={this._usernameChanged.bind(this)}/> </View> <Button title={"Submit"} onPress={this._submit.bind(this)}/> </View> ); } }
現在的代碼看起來更多了 – 因為我們為Avatar,FormItem和Button組件添加了包裝器,但現在我們可以在需要的地方重復使用這些組件。我們可以將這些組件移動到單獨的模塊中,并導入我們需要的任何地方。我們也可以添加其他一些Props,例如style,TextStyle,onLongPress,onBlur,onFocus。而且這些組件是完全可以定制的。
注意,一定不要深度定制一個小組件, 這樣會使組件過于繁瑣,代碼會變的很難閱讀。即使現在添加新屬性的想法看起來像是解決任務的最簡單的方法,將來這個小小的屬性在閱讀代碼時可能會引起困惑。
關于理想的smart/dumb組件,看一下這個:
class Button extends Component{ constructor(props){ super(props); } _setTitle(){ const { id } = this.props; switch(id){ case 0: return 'Submit'; case 1: return 'Draft'; case 2: return 'Delete'; default: return 'Submit'; } } render(){ let title = this._setTitle(); return( <TouchableOpacity onPress={this.props.onPress}> <View style={styles.btn}> <Text style={styles.btnText}> {title} </Text> </View> </TouchableOpacity> ) } } Button.propTypes = { id: PropTypes.number, onPress: PropTypes.func.isRequired } export default class SomeContainer extends Component { constructor(props){ super(props); this.state = { username:null } } _submit(){ if(this.state.username){ console.log(`Hello, ${this.state.username}!`); } else{ console.log('Please, enter username'); } } render() { return ( <View style={styles.container}> <Button id={0} onPress={this._submit.bind(this)}/> </View> } }
我們已經“升級”了Button組件。用一個叫做“id”的新屬性來替換屬性“title”。現在Button組件就變的“靈活”了。傳0 – button組件會顯示“submit”。傳2 – 顯示“delete”。但這可能會有一些問題。
Button被創建為一個dumb組件 – 只是為了顯示數據,傳遞數據這件事由它的更高一級的組件來完成。
如果我們將5作為id傳遞給這個組件,我們就需要更新組件,以便讓它適應這個改動。dumb組件,就是細分的小組件,它只要接收props就好了,如果有state也應該與全局的無關。
7.行內樣式
在使用RN布局之后,我遇到了行內樣式的寫作風格問題。類似這樣:
render() { return ( <View style={{flex:1, flexDirection:'row', backgroundColor:'transparent'}}> <Button title={"Submit"} onPress={this._submit.bind(this)}/> </View> ); }
當你這樣寫的時候,你會想:“暫時這樣寫,等我在模擬器中運行之后 – 如果布局沒問題,再把樣式移動到單獨的模塊。”也許這是一個好的想法。但是..不幸的是,你往往會選擇性忽略行內樣式…
一定要在獨立的模塊中編寫樣式,遠離行內樣式。
8.使用redux驗證表單
要使用redux來驗證表單,我需要在reducer中創建action,actionType單獨的字段,這樣做很麻煩。
所以我決定只借助state來完成驗證。沒有reducers,types等等,只是在container級別上的純功能函數。從action和reducer文件中刪除不必要的函數,這個策略對我幫助很大。
9.過于依賴zIndex
很多人從web開發轉到RN開發。在web中有一個css屬性z-index,它可以幫助我們在需要的層級顯示我們想要的內容。
在RN中,一開始沒有這樣的特性。但后來又被添加進來了。起初,使用起來還挺簡單的。只需為元素設置zIndex屬性,它就會按照任何你想要的圖層順序來渲染。但是在Android上測試之后…現在我只用zIndex來設置展示層的結構。
10.不仔細閱讀外部組件的源碼
你可以引入外部組件來節省你的開發時間。
但有時這個模塊可能會中斷,或者不像描述的那樣工作。閱讀源碼你才會明白哪里出現了錯誤。也許這個模塊本身就有問題,或者你只是用錯了。另外 – 如果你仔細閱讀其他模塊的源碼,你將會學習到如何構建自己的組件。
11.要小心手勢操作和Animated API。
RN為我們提供了構建完全原生應用程序的能力。怎么讓用戶感覺是原生應用?頁面布局,滑動手勢,還是展示動畫?
當你使用View,Text,TextInput和其他RN提供的默認模塊時,手勢和動畫應該由PanResponder和Animated API來處理。
如果你是從web轉過來的rn開發工程師,獲取用戶的手勢操作可能會有些困難,你需要區分什么時候開始,什么時候結束,長按,短按。你可能還不夠清楚怎么在RN中模擬這些動畫操作。
這是我用PanResponder和Animated建立的Button組件。這個button是為了捕捉用戶手勢而構建的。例如 – 用戶按下項目,然后將手指拖到一邊。在按下按鈕時,借助于動畫API,構建button按壓下的不透明度的變化:
'use strict'; import React, { Component, PropTypes } from 'react'; import { Animated, View, PanResponder, Easing } from 'react-native'; import moment from 'moment'; export default class Button extends Component { constructor(props){ super(props); this.state = { timestamp: 0 }; this.opacityAnimated = new Animated.Value(0); this.panResponder = PanResponder.create({ onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, onStartShouldSetResponder:() => true, onStartShouldSetPanResponder : () => true, onMoveShouldSetPanResponder:(evt, gestureState) => true, onPanResponderMove: (e, gesture) => {}, onPanResponderGrant: (evt, gestureState) => { /**THIS EVENT IS CALLED WHEN WE PRESS THE BUTTON**/ this._setOpacity(1); this.setState({ timestamp: moment() }); this.long_press_timeout = setTimeout(() => { this.props.onLongPress(); }, 1000); }, onPanResponderStart: (e, gestureState) => {}, onPanResponderEnd: (e, gestureState) => {}, onPanResponderTerminationRequest: (evt, gestureState) => true, onPanResponderRelease: (e, gesture) => { /**THIS EVENT IS CALLED WHEN WE RELEASE THE BUTTON**/ let diff = moment().diff(moment(this.state.timestamp)); if(diff < 1000){ this.props.onPress(); } clearTimeout(this.long_press_timeout); this._setOpacity(0); this.props.releaseBtn(gesture); } }); } _setOpacity(value){ /**SETS OPACITY OF THE BUTTON**/ Animated.timing( this.opacityAnimated, { toValue: value, duration: 80, } ).start(); } render(){ let longPressHandler = this.props.onLongPress, pressHandler = this.props.onPress, image = this.props.image, opacity = this.opacityAnimated.interpolate({ inputRange: [0, 1], outputRange: [1, 0.5] }); return( <View style={styles.btn}> <Animated.View {...this.panResponder.panHandlers} style={[styles.mainBtn, this.props.style, {opacity:opacity}]}> {image} </Animated.View> </View> ) } } Button.propTypes = { onLongPress: PropTypes.func, onPressOut: PropTypes.func, onPress: PropTypes.func, style: PropTypes.object, image: PropTypes.object }; Button.defaultProps = { onPressOut: ()=>{ console.log('onPressOut is not defined'); }, onLongPress: ()=>{ console.log('onLongPress is not defined'); }, onPress: ()=>{ console.log('onPress is not defined'); }, style: {}, image: null }; const styles = { mainBtn:{ width:55, height:55, backgroundColor:'rgb(255,255,255)', } };
首先,初始化PanResponder對象實例。它有一套不同的操作句柄。我感興趣的是onPanResponderGrand(當用戶觸摸按鈕時觸發)和onPanResponderRelease(當用戶從屏幕上移開手指時觸發)兩個句柄;
我還設置了一個動畫對象實例,幫助我們處理動畫。將其值設置為零;然后我們定義_setOpacity方法,調用時改變this.opacityAnimated的值。在渲染之前,我們插入this.opacityAnimated為正常的opacity值。我們不使用View而是使用Animated.View模塊為了使用動態變化的opacity值。
通過上面的例子,你會發現Animated API不難理解,你只需要閱讀相關的API文檔,以確保你的應用程序***運行。希望這個例子能幫你開個好頭。
以上是“使用React Native/Redux開發中會遇到什么錯誤”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。