學過React Native的都知道,RN的UI是根據相應組件的state進行render的,而頁面又是由大大小小的組件構成,導致每個組件都必須維護自身的一套狀態,因此當頁面復雜化的時候,管理state會相當吃力的。而redux提供了一套機制來組織管理整個應用狀態。
? ? ? ?Redux有三部分組成:store,action,reducer。
? ? ? ?store:維護全局的state,以及將action和reducer結合起來。
? ? ? ?action:用來傳遞state的信息。(比如:我們在action中處理登陸操作,將返回的user對象傳遞給對應的reducer.)
? ? ? ?reducer:reducer是簡單的處理函數,通過傳入舊的state和指示操作的action來更新state,從而達到頁面的刷新。
下面通過一個簡單的例子來集成下。
首先安裝相關庫:
?
安裝redux:npm install --save redux
安裝redux綁定庫:npm install --save react-redux
安裝開發者工具:npm install --save-dev redux-devtools
安裝異步action構造器:npm install --save redux-thunk
在集成之前熟悉下一般基于Redux的目錄結構:
?
(如果你之前不了解Redux的話,或許會比較蒙圈,但不要緊,跟著流程多走幾遍,試著推敲先分析下流程,慢慢就理解了)
/*************************************store*************************************/
1.首先創建全局的store。(一般在stores文件中寫個配置文件)
?
- 'use?strict';??
- ??
- import?{?createStore,?applyMiddleware?,combineReducers}?from?'redux';??
- import?thunk?from?'redux-thunk';//引入異步操作??
- //引入所有的reducers,切記要在index.js封裝下.??
- import?*?as?reducers?from?'../reducers';??
- const?middlewares?=?[thunk];??
- ??
- const?createSoreWithMiddleware=applyMiddleware(...middlewares)(createStore);??
- ??
- //配置store信息??
- export?default?function?configureStore(initialState){??
- ??
- ??//將reducer組合起來??
- ??const?reducer=combineReducers(reducers);??
- ??//創建store??
- ??const?store=createSoreWithMiddleware(reducer,initialState);??
- ????
- ??return?store;??
- }??
簡單講解下:
首先引入該APP中所有的reducer,根據上面的目錄結構,我們把所有的reducer放入到reducers文件中,切記要加入個index.js進行配置.上面很多都是固定格式,暫時先不分析為什么,做的目的就是返回一個全局的store.
store的應用(這里的APP就是我們應用的最頂層組件)。
?
- import?React,?{?Component?}?from?'react';??
- import?{Provider}?from?'react-redux';??
- import?App?from?'./containers/app';??
- ??
- import?configureStore?from?'./store/configureStore';??
- const?store=configureStore();//獲取store??
- ??
- export?default?class?Root?extends?Component{??
- ???render(){??
- ?????return(??
- ???????<Provider?store={store}>??
- ???????<App/>??
- ???????</Provider>??
- ?????);??
- ???}??
- }??
App組件其實可以把所有的頁面加入到這里,全局進行控制(官方F8是這么操作的)。不過這里暫時先不這樣處理,關于navigator的push,pop操作還是放到對應的頁面進行處理,更符合我們的原生開發邏輯。
簡單看下store中結構:
?
/*************************************action*************************************/
創建登陸對應的action:
?
- ?import?*?as?types?from?'./types';??
- ?import?{Alert}from?'react-native';??
- ??
- //登陸(登陸操作屬于耗時操作,所以需要異步執行,這里采用dispatch分發)??
- export?function?login(user){??
- ???return?dispatch=>{??
- ?????//登陸中,派遣給LOGIN_ING??
- ?????dispatch({type:types.LOGIN_ING});??
- ?????let?result=fetch('http://www.baidu.com')??
- ????????????????.then((res)=>{??
- ??????????????????//延時2s為了模擬效果??
- ??????????????????setTimeout(()=>{??
- ????????????????????if(user.phone=='15221850400'&&user.password=='123456'){??
- ??????????????????????dispatch({type:types.LOGIN,user:user});??
- ????????????????????}else{??
- ??????????????????????//這里分發的是action??
- ??????????????????????Alert.alert('用戶名或密碼錯誤');??
- ??
- ??????????????????????dispatch(error());??
- ????????????????????}??
- ??????????????????},1000);??
- ????????????????}).catch((err)=>{??
- ???????????????????alert(err);??
- ???????????????????dispatch({type:types.LOGIN_ERROR});??
- ????????????????})??
- ???}??
- }??
- ??
- function?error(){??
- ??return?{??
- ????type:types.LOGIN_ERROR??
- ??};??
- }??
- ??
- //登出(由于登出操作一般都只是清空一些數據,不需要異步執行直接返回就可以了,)??
- export?function?logout(){??
- ??return?{??
- ????type:types.LOGOUT,??
- ??};??
- }??
邏輯還算簡單,就只是做個用戶名,密碼判斷,但或許會問dispatch是個什么玩意,哪來的呢,其實上面我們也截圖出來了,這個方法是我們創建全局store中的方法。
至于action正常的應該只是一個含有type的json對象,但是為了擴展性,一般會寫成函數的形式,俗稱action creator如上面的logout方法.
至于login方法由于需要網絡操作,固然是異步的,就好比我們原生開發的時候請求API的操作一般都會丟到一個線程中,通過Handler消息機制來渲染UI.
dispatch({type:types.LOGIN_ING}):根據相應的action來進行調用對應reducer方法。
/*************************************reducer*************************************/
接著我們看下最后一個reducer:
?
- import?*?as?types?from?'../actions/types';??
- ??
- const?initialState={??
- ??isLoggedIn:false,//登陸狀態??
- ??user:{},??
- ??status:?null,//登陸操作狀態?‘done’:已登陸,'doing':正在登陸,null:沒有登陸??
- };??
- ??
- //reducer處理函數更新state,渲染UI(主要根據傳入舊的state,)??
- export?default?function?user(state=initialState,action={}){??
- ??
- ??switch(action.type)?{??
- ????case?types.LOGIN:??
- ???????return{??
- ?????????...state,??
- ?????????isLoggedIn:true,??
- ?????????user:action.user,??
- ?????????status:?'done',??
- ???????}??
- ??????break;??
- ????case?types.LOGIN_ING:??
- ??????return?{??
- ????????...state,??
- ????????isLoggedIn:false,??
- ????????status:?'doing',??
- ??????}??
- ??????break;??
- ????case?types.LOGIN_ERROR:??
- ????console.log('types.LOGIN_ERROR...');??
- ????????return{??
- ??????????...state,??
- ????????????isLoggedIn:?false,??
- ??????????status:?null,??
- ????????};??
- ????????break;??
- ????case?types.LOGOUT:??
- ??
- ??????return?{??
- ????????...state,??
- ????????isLoggedIn:false,??
- ????????status:null,??
- ??????}??
- ??????break;??
- ????//切莫忘記default返回值??
- ????default:??
- ??????return?state;??
- ??}??
- }??
reducer其實就是根據一系列action的處理函數,好比我們在前面action中返回的有LOGIN,LOGIN_ING,LOGIN_ERROR等狀態,然后調用reducer根據不同的type返回當前最新的state,然后再render ui。
/*************************************connect*************************************/
redux的三部分至此就操作完畢,但如果進行鏈接起來呢,這里就用到connect組件,connect是將某一個組件(這里一般指一個頁面)和store鏈接起來,目的就是獲取當前頁面所需的state以及dispatch方法。(從全局state中獲取這個頁面需要的數據然后以props的形式傳遞給當前頁面。)
?
- import?React,?{?Component?}?from?'react';??
- import?{??
- ??StyleSheet,??
- ??TextInput,??
- ??Text,??
- ??View,??
- ??TouchableHighlight,??
- ??ActivityIndicator,??
- }?from?'react-native';??
- ??
- import?{connect}?from?'react-redux';//將我們的頁面和action鏈接起來??
- import?{bindActionCreators}?from?'redux';//將要綁定的actions和dispatch綁定到一起??
- import?*?as?actionCreators?from?'../actions/loginActions';//導入需要綁定的actions??
- import?Modal?from?'react-native-modalbox';??
- import?Home?from?'./home';??
- ??
- ??
- /**?
- 登陸頁面?
- **/??
- class?Login?extends?Component{??
- ??
- ??constructor(props){??
- ????super(props);??
- ??
- ????this.state={??
- ????}??
- ??
- ????this.login=this.login.bind(this);??
- ????this.onChangePhone=this.onChangePhone.bind(this);??
- ????this.onChangePswd=this.onChangePswd.bind(this);??
- ??}??
- ??
- ???onChangePhone(text){??
- ?????this.setState({'phone':text,});??
- ???}??
- ??
- ???onChangePswd(text){??
- ?????this.setState({'password':text,});??
- ???}??
- ??
- ???login(){??
- ??
- ?????if(!this.state.phone||!this.state.password){??
- ???????alert('用戶名或密碼不能為空!');??
- ?????}else{??
- ???????this.refs.modal.open();//loading?狀態??
- ???????this.props.actions.login({'phone':this.state.phone,'password':this.state.password});//dispath?登陸??
- ?????}??
- ???}??
- ??
- ???//該方法首次不會執行,如果返回false,則reduer不會執行,,??
- ???shouldComponentUpdate(nextProps,nextState){??
- ?????const?{isLoggedIn,navigator}=nextProps;??
- ??????if(isLoggedIn){??
- ????????this.setState({phone:'',password:''});??
- ??
- ????????navigator.push({??
- ??????????component:Home,??
- ??????????name:'Home',??
- ????????});??
- ??????}??
- ?????return?true;??
- ???}??
- ??
- ???render(){??
- ????console.log('render...');??
- ?????return(??
- ??????<View?style={{flex:1}}>??
- ??????<View?style={{padding:20,marginTop:50}}>??
- ??????<View?style={styles.item}><Text?style={{width:70}}>手機號碼</Text>??
- ??????<TextInput??
- ??????style={styles.input}??
- ??????onChangeText={this.onChangePhone}??
- ??????placeholder='請輸入手機號碼'??
- ??????value={this.state.phone}??
- ??????/>??
- ??????</View>??
- ??????<View?style={styles.item}>??
- ??????<Text?style={{width:70}}>密碼</Text>??
- ??????<TextInput??
- ??????style={styles.input}??
- ??????onChangeText={this.onChangePswd}??
- ??????placeholder='請輸入密碼'??
- ??????password={true}??
- ??????value={this.state.password}??
- ??????/>??
- ??????</View>??
- ??
- ??????<TouchableHighlight?style={styles.button}??
- ???????underlayColor='#000000'?onPress={this.login}>??
- ??????<Text?style={{fontSize:16,color:'#fff'}}>登陸</Text>??
- ??????</TouchableHighlight>??
- ??????</View>??
- ??
- ??????<Modal??
- ??????style={styles.modal}??
- ??????ref='modal'??
- ??????isOpen={this.props.status=='doing'?true:false}??
- ??????animationDuration={0}??
- ??????position={"center"}??
- ??????>??
- ??????<ActivityIndicator??
- ??????size='large'??
- ??????/>??
- ??????<Text?style={{marginTop:15,fontSize:16,color:'#444444'}}>登陸中...</Text>??
- ??????</Modal>??
- ??????</View>??
- ?????);??
- ???}??
- }??
- ??
- const?styles?=StyleSheet.create({??
- ????item:{??
- ??????flex:1,??
- ??????flexDirection:'row',??
- ??????alignItems:'center',??
- ??????height:50,??
- ??????borderBottomColor:'#ddd',??
- ??????borderBottomWidth:1,??
- ????},??
- ????input:{??
- ??????flex:1,??
- ??????fontSize:14,??
- ????},??
- ????button:{??
- ??????backgroundColor:'#1a191f',??
- ??????height:50,??
- ??????marginTop:40,??
- ??????justifyContent:'center',??
- ??????alignItems:'center'??
- ????},??
- ????modal:?{??
- ??????justifyContent:?'center',??
- ??????alignItems:?'center',??
- ??????width:150,??
- ??????height:150,??
- ??????borderRadius:10,??
- ????},??
- });??
- ??
- //根據全局state返回當前頁面所需要的信息,(注意以props的形式傳遞給Login)??
- function?mapStateToProps(state){??
- ??return{??
- ????isLoggedIn:state.user.isLoggedIn,??
- ????status:state.user.status,??
- ??};??
- }??
- //返回可以操作store.state的actions,(其實就是我們可以通過actions來調用我們綁定好的一系列方法)??
- function?mapDispatchToProps(dispatch){??
- ??return?{??
- ??????actions:?bindActionCreators(actionCreators,?dispatch)??
- ??};??
- }??
- ??
- //鏈接起來??
- export?default?connect(mapStateToProps,mapDispatchToProps)(Login);??
上面的代碼不用仔細看,主要是尾部的部分,
mapStateToProps方法:根據全局state返回當前頁面所需的數據然后以props的形式傳遞給當前頁面(Login)。
mapDispatchToProps:該方法就是將dispatch和當前頁面引入的actionCreators綁定在一起,然后就可以輕松調用。
如:login方法中的:
this.props.actions.login({'phone':this.state.phone,'password':this.state.password});//dispath 登陸
?
這樣整體集成就完畢了,這里簡單總結下:
1.首先我們創建全局的store(基于所有的reducer)在APP最外層引用,然后我們創建action(可以根據頁面或者某種類別來定義)。接著我們創建reducer(可以設計成跟action一一對應)。最后通過connect將它們和頁面鏈接起來,至于action和reducer的內容,可以等頁面編輯OK后再進行設計。
2.執行簡單流程:在頁面中首先調用對應action方法(傳遞參數)--->執行相應的業務邏輯,然后調用dispatch(action)(將結果以action的形式傳遞給reducer)--->在reducer中根據type字段然后返回最新的state,然后在進行render具體的ui。
?
引用原文:https://blog.csdn.net/jj120522/article/details/52071469
可以參考:https://www.jianshu.com/p/4139babc6d5e
?
寫博客是為了記住自己容易忘記的東西,另外也是對自己工作的總結,文章可以轉載,無需版權。希望盡自己的努力,做到更好,大家一起努力進步!
如果有什么問題,歡迎大家一起探討,代碼如有問題,歡迎各位大神指正!