ReactNative之Redux入门(2)- 初步使用教程

注:本教程针对于有过React/ReactNative开发经验的人群。

redux的用法很灵活,根据项目需求,先来学习针对于react-native的基础用法。
[redux] + [react-redux] + [redux-thunk]

我在整理之前原本是像直接讲redux,但是觉得讲了也都不会,因为我是会用之后,过了好几天才捋清楚基础知识的,所以就直接先讲用法。等会用之后,再深入学习。
先简述一下:
redux:不用说,本场主角,上一节已经详细介绍了。
react-redux:这是redux作者针对于react-native封装的。
redux-thunk:处理异步。(中间件-还有很多中间件)。

所以在进行以下步骤的编码过程中,先不需要理解,等实现了功能再说。后续章节将会详细的来讲解用法和意义。
1.创建项目。

react-native init ReactReduxDemo --version 0.51.0

2.导入库。

npm install --save redux
npm install --save react-redux
npm install --save redux-thunk
npm install --save react-navigation

注:react-navigation是做路由用的,与redux无关。

3.创建文件和文件夹。
在项目根目录下创建app文件夹,名字随便起,用于统一存放和管理代码。

在app目录下分别创建actions、reducers、store文件夹。选择创建page文件夹用来存放普通页面。并创建Root.js、ActionType.js文件。

画红线的是必须要有的。

4.编写action、reducer、stort、Root.js、ActionType.js。

那么就让我们做一个获取天气预报信息,然后发送给任意页面,从任意页面接收并灵活修改state的小例子吧。

按部就班,让我们一步一步来。ps:有道云对代码支持不是很好,先凑合着看,如果有什么让有道云可以对代码进行友好支持的工具,希望分享给我哈,谢谢。
看到这里如果不理解自己在做什么就不要去理解了,先跟着我写代码。


---------------------------分界线---------------------------
ActionType是全部type的集合,用于让reducer判断到底是哪个action发过来的。大白话来说就是给action起个名字,让reducer根据名字找到action。
ActionType.js
/**
* create by AbyssKitty on 2018/01/18
* 所有的Action的type的集合
*/

//export const LOGIN_DENGLU = 'login_denglu'; //初始化状态
export const ACTION_GETWEATHER_INIT = ' action_getweather_init '; //
export const ACTION_GETWEATHER_SUCCESS = ' action_getweather_success '; //


---------------------------分界线---------------------------
打开并在actions文件夹内新建文件GetWeatherAction.js。创建一个用来获取天气信息的action。
GetWeatherAction.js
/**
* create by AbyssKitty on 2017/12/06
* 获取天气预报的action
*/
import * as TYPES from ' ../ActionType ';
/**
* 获取天气预报的action
*/
export function actionGetWeather ( list ){
return ( dispatch ) => {
//开始获取,发送一个dispatch
dispatch ( init ( list )) ;
/**
* 在这里假装做了一个类似于调接口的操作
*/
//获取成功,发送一个dispatch
dispatch ( success ( list )) ;
}
}

/**
* 这里会通过dispatch把action送给reducer,TYPE是判断拿到的是哪个action。
*/
function init ( list ){
return {
type : TYPES . ACTION_GETWEATHER_INIT ,
message : ' 开始获取 ',
bean : list ,
}
}

function success ( list ){
return {
type : TYPES . ACTION_GETWEATHER_SUCCESS ,
message : ' 获取成功 ',
bean : list ,
}
}


---------------------------分界线---------------------------
然后我们在reducers文件夹下新建一个item文件夹和一个IndexReducers.js文件
在item文件夹下创建一个reducer文件,并命名GetWeatherReducer.js(注item下存放所有的reducer文件,并必须在IndexReducers.js中全部配置)
如图所示:
在reducers的item中存放所有的reducer文件。然后再IndexReducers.js统一处理。
GetWeatherReducer.js
/**
* create by AbyssKitty on 2017/12/06
* reducers
*/
import * as TYPES from ' ../../ActionType ';

/**
* 这里可以初始化一个默认的实体类
*/
const initialState = {
status : ' init ',
isSuccess : false ,
bean : null ,
message : '',
}
/**
* 在这里可以拿到action并return给IndexReducers.js进行分发。
*
* 根据type判断了是从哪个action过来的数据,并进行选择性return。
*/
export default function getWeather ( state = initialState , action ) {
switch ( action . type ) {
case TYPES . ACTION_GETWEATHER_INIT : // 初始状态
return Object . assign ( {}, state , {
status : ' init ',
isSuccess : false ,
bean : action . bean ,
message : action . message ,
} ) ;
break ;
case TYPES . ACTION_GETWEATHER_SUCCESS : // 初始状态
return Object . assign ( {}, state , {
status : ' success ',
isSuccess : true ,
bean : action . bean ,
message : action . message ,
} ) ;
break ;
default :
return state ;
}
}

----------------
IndexReducers.js
/**
* create by AbyssKitty on 2017/09/22
* 事件分发 总模块
*/

import { combineReducers } from ' redux ';
import GetWeatherReducer from ' ./item/GetWeatherReducer ';

//这里面必须要有初始数据 - 否则报错
const rootReducer = combineReducers ( {
//GetWeatherReducer : GetWeatherReducer,
GetWeatherReducer ,
} ) ;

export default rootReducer ;

---------------------------分界线---------------------------

在store文件夹中新建Store.js文件,用来初始化配置redux。
Store.js
/**
* create by AbyssKitty on 2017/12/06
* store 的配置文件
*/

import { createStore , applyMiddleware } from ' redux ';
import thunkMiddleware from ' redux-thunk ';
import rootReducer from ' ../reducers/IndexReducers ';

/**
* applyMiddleware是一个 柯里化(Currying)函数,使用中间件redux-thunk
* 最后返回一个store
*/

const createStoreWithMiddleware = applyMiddleware ( thunkMiddleware )( createStore ) ;

export default function configureStore ( initialState ) {
const store = createStoreWithMiddleware ( rootReducer , initialState ) ;
return store ;
}

---------------------------分界线---------------------------
编辑Root.js文件通过Provider包裹整个app。我这里包裹的是navigation。
Root.js
/**
* create by AbyssKitty on 2017/12/06
* 程序入口 通过包裹初始化
*/
import React , { Component } from ' react ';
import { Provider } from ' react-redux ';
import configureStore from ' ./store/Store ';
import { AppNavigator } from ' ./navigator/navigator ';

const store = configureStore () ;

/**
* react-redux介绍
* 阮一峰博客:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html
* React-Redux 将所有组件分成两大类:UI 组件(presentational component)和容器组件(container component)。
* UI:
* 只负责 UI 的呈现,不带有任何业务逻辑
* 没有状态(即不使用this.state这个变量)
* 所有数据都由参数(this.props)提供
* 不使用任何 Redux 的 API
* 容器:
* 负责管理数据和业务逻辑,不负责 UI 的呈现
* 带有内部状态
* 使用 Redux 的 API
*/
/**
* Provider详解。
* 他是react-redux提供的,用来让子组件可以通过props直接拿到state了。
* (注:如果不用react-redux,就需要一层一层的传值,十分繁琐。)
* Provider用法。用他包裹住整个app组件,他的子组件就全部可以通过props拿到state了。
*/
export default class Root extends Component {
render () {
return (
< Provider store ={ store } >
< AppNavigator />
</ Provider >
) ;
}
}

---------------------------分界线---------------------------
然后就可以在App.js(V >= 0.50.0) || index.android.js/index.ios.js (V < 0.50.0)
中使用这个Root.js。 把Root.js用作程序的入口。
App.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*
* create by AbyssKitty on 2018-04-11
*/

import React , { Component } from ' react ';
import {
Platform ,
StyleSheet ,
Text ,
View
} from ' react-native ';
import Root from ' ./app/Root ';

export default class App extends Component {
render () {
return (
< Root />
) ;
}
}

整个redux的流程就编写完成了。接下来开始使用redux。

5.编写Demo。
因为需要跳转页面所以在这里我使用了navigation
navigator.js
import React from ' react '
import { StackNavigator } from ' react-navigation '

import Main from ' ../page/Main ';
import Redux1 from ' ../page/Redux1 ';

export const AppNavigator = StackNavigator ( {

Main : { screen : Main },
Redux1 : { screen : Redux1 },
} ) ;

---------------------------分界线---------------------------
导航中分别设置了两个页面Main.js和Redux1.js
先贴出代码:在看过代码之后我会依次讲解。
Main.js
import React , { Component } from ' react ';
import {
StyleSheet ,
Text ,
View ,
Image ,
TouchableHighlight ,
} from ' react-native ';

import { connect } from ' react-redux ';

class Main extends Component {
static navigationOptions = ({ navigation , screenProps }) => ( {
header : null ,
} )

constructor ( props ) {
super ( props ) ;
//设置状态
this . state = {
image : 1 ,
text : '',
}
}

goReduxPage = () => {
this . props . navigation . navigate ( " Redux1 " ) ;
}

goPropsPage = () => {

}

componentDidMount (){
this . setState ( {
text : '',
} )
}

quit = () => {
this . setState ( {
text :'',
image : 1 ,
} )
}

/**
* 生命周期 - props发生变动时的操作,建议将回调代码在这里处理
* @ param { * } nextProps
*/
componentWillReceiveProps ( nextProps ){
if ( nextProps . GetWeatherReducer != null ) {
if ( nextProps . GetWeatherReducer . status == ' success ' ) {
this . setState ( {
image : 2 ,
} )
} else {
this . setState ( {
image : 1 ,
} )
}
this . setState ( {
text : nextProps . GetWeatherReducer . status ,
} )
}
}

/**
* 生命周期 - 因为state变动频繁,不建议将redux的回调代码在这里处理,而且这里面是不能进行setState的操作的。
* @ param { * } nextProps
* @ param { * } nextState
*/
shouldComponentUpdate ( nextProps , nextState ){
console . log ( " 执行了shouldComponentUpdate " ) ;
}

render () {

return (
< View style = { styles . bgView } >
< Text >
{
" 本Demo主要功能: \n"+
" 1.redux在本页面或者跨页面修改全局组件内容 \n"+
" 2.通过props,state父子组件交互 "
}
</ Text >
< TouchableHighlight style = { styles . touchableView } underlayColor ={" #f4f4f4 "} onPress ={() => this . goReduxPage () } >
< View style = { styles . buttonView } >
< Text style = {{ fontSize : 16 , color : " #FFFFFF " }} >
{ ' redux ' }
</ Text >
</ View >
</ TouchableHighlight >

< TouchableHighlight style = { styles . touchableView } underlayColor ={" #f4f4f4 "} onPress ={() => this . goPropsPage () } >
< View style = { styles . buttonView } >
< Text style = {{ fontSize : 16 , color : " #FFFFFF " }} >
{ ' props ' }
</ Text >
</ View >
</ TouchableHighlight >

{ /* 动态区域 */ }
< View style = {{ width : ' 100% ', height : 100 , backgroundColor : " EAEEEF " , justifyContent : ' center ', alignItems : ' center ',}} >
{
this . state . image == 1 ?
< Image source = { require ( " ../images/heimao.png " ) }
style = {{ width : 40 , height : 40 , borderRadius : 20 }} />
:
< Image source = { require ( " ../images/bgshuiguo.png " ) }
style = {{ width : 40 , height : 40 , borderRadius : 20 }} />
}
< Text style = {{ color :" FF0000 ", fontSize : 14 }} >
{ this . state . text }
</ Text >
</ View >

< TouchableHighlight style = { styles . touchableView } underlayColor ={" #f4f4f4 "} onPress ={() => this . quit () } >
< View style = { styles . buttonView } >
< Text style = {{ fontSize : 16 , color : " #FFFFFF " }} >
{ ' clear ' }
</ Text >
</ View >
</ TouchableHighlight >
</ View >
) ;
}
}

const styles = StyleSheet . create ( {
bgView : {
flex : 1 ,
justifyContent : ' center ',
alignItems : ' center ',
backgroundColor : ' #FFFFFF ',
},
touchableView : {
margin : 6 ,
},
buttonView : {
width : 100 ,
height : 40 ,
backgroundColor : " #0000FF ",
borderRadius : 5 ,
flexDirection : ' row ',
justifyContent : ' center ',
alignItems : ' center ',
},
} ) ;

function select ( store ) {
return {
GetWeatherReducer : store . GetWeatherReducer ,
}
}

export default connect ( select )( Main ) ;

---------------------------分界线---------------------------
Redux1.js
import React , { Component } from ' react ';
import {
StyleSheet ,
Text ,
View ,
TouchableHighlight ,
} from ' react-native ';

import { connect } from ' react-redux ';
import { actionGetWeather } from ' ../actions/GetWeatherAction ';

class Redux1 extends Component {
static navigationOptions = ({ navigation , screenProps }) => ( {
header : null ,
} )

constructor ( props ) {
super ( props ) ;
//设置状态
this . state = {
}
}

quit = () => {
this . props . navigation . goBack () ;
}

stateRedux = () => {
let s = ' 123456 ';
this . props . dispatch ( actionGetWeather ( s )) ;
}

render () {
return (
< View style = { styles . bgView } >
< TouchableHighlight style = { styles . touchableView } underlayColor ={" #f4f4f4 "} onPress ={() => this . stateRedux () } >
< View style = { styles . buttonView } >
< Text style = {{ fontSize : 16 , color : " #FFFFFF " }} >
{ ' 发送 ' }
</ Text >
</ View >
</ TouchableHighlight >

< Text style = {{ color :" #000000 ", fontSize : 14 }} >
{
this . props . GetWeatherReducer == null ?
""
:
this . props . GetWeatherReducer . status
}
</ Text >

< TouchableHighlight style = { styles . touchableView } underlayColor ={" #f4f4f4 "} onPress ={() => this . quit () } >
< View style = { styles . buttonView } >
< Text style = {{ fontSize : 16 , color : " #FFFFFF " }} >
{ ' quit ' }
</ Text >
</ View >
</ TouchableHighlight >
</ View >
) ;
}
}

const styles = StyleSheet . create ( {
bgView : {
flex : 1 ,
justifyContent : ' center ',
alignItems : ' center ',
backgroundColor : ' #FFFFFF ',
},
touchableView : {
margin : 6 ,
},
buttonView : {
width : 100 ,
height : 40 ,
backgroundColor : " #0000FF ",
borderRadius : 5 ,
flexDirection : ' row ',
justifyContent : ' center ',
alignItems : ' center ',
},
} ) ;

function select ( store ) {
return {
GetWeatherReducer : store . GetWeatherReducer ,
}
}

export default connect ( select )( Redux1 ) ;

使用讲解:
1.要在类中拿到redux只需要引入一个包。
import { connect } from 'react-redux';

2.要发起一个action需要导入connect和自定义的action。
import { connect } from 'react-redux';
import { actionGetWeather } from '../actions/GetWeatherAction';

3.用connect把状态和UI进行绑定
function select ( store ) {
return {
GetWeatherReducer : store . GetWeatherReducer ,
}
}

export default connect ( select )( Redux1 ) ;

------------------
class Redux1 extends Component {
------------------
注:名称对应,export default只能导出一个。
简述:connect的第一个括号就是状态,第二个括号就是UI。(下一节将详细讲解)

4.发送一个action
this . props . dispatch ( actionGetWeather ( s )) ;

5.接收state(方式一)
注意:react-redux是改变的props,所以要使用props, nextProps . GetWeatherReducer 进行抓取数据,一定要做非空判断。
/**
* 生命周期 - props发生变动时的操作,建议将回调代码在这里处理
* @ param { * } nextProps
*/
componentWillReceiveProps ( nextProps ){
if ( nextProps . GetWeatherReducer != null ) {
if ( nextProps . GetWeatherReducer . status == ' success ' ) {
this . setState ( {
image : 2 ,
} )
} else {
this . setState ( {
image : 1 ,
} )
}
this . setState ( {
text : nextProps . GetWeatherReducer . status ,
} )
}
}

/**
* 生命周期 - 因为state变动频繁,不建议将redux的回调代码在这里处理,而且这里面是不能进行setState的操作的。
* @ param { * } nextProps
* @ param { * } nextState
*/
shouldComponentUpdate ( nextProps , nextState ){
console . log ( " 执行了shouldComponentUpdate " ) ;
}

6.接收state(方式二)
直接可以再代码中使用!
< Text style = {{ color :" #000000 ", fontSize : 14 }} >
{
this . props . GetWeatherReducer == null ?
""
:
this . props . GetWeatherReducer . status
}
</ Text >

两种使用方式的区别?
方式一在生命周期中监听,只有生命周期发生变化,才会调用。
方式二直接使用,state发生更改的同时,他就会发生变化。


贴出运行示例

1.运行


2.点击redux跳转到Redux1.js页面


3.点击发送,发送一个action,然后点击quit返回上一页面查看效果。


图像和text就都发生变化了。
Redux1.js页面因为是使用方式二直接使用的,所以状态一直是success。
所有操作过程都在action和reducer中进行操作。

基本使用就都这里结束了。

下一节将详细讲解运行机制,redux详解 和 react-redux详解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值