注:本教程针对于有过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中全部配置)
如图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/eec474191323dcab4f87d8748ae6bf16.jpeg)
在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.运行
![](https://i-blog.csdnimg.cn/blog_migrate/3ffca4b07eebe67e14445741f29ad4f3.jpeg)
2.点击redux跳转到Redux1.js页面
![](https://i-blog.csdnimg.cn/blog_migrate/e5e765feb7d4bb1b97d271bf2025fdc8.jpeg)
3.点击发送,发送一个action,然后点击quit返回上一页面查看效果。
![](https://i-blog.csdnimg.cn/blog_migrate/4aa9e340cc555f853c306328e4de0d77.jpeg)
图像和text就都发生变化了。
Redux1.js页面因为是使用方式二直接使用的,所以状态一直是success。
所有操作过程都在action和reducer中进行操作。
基本使用就都这里结束了。
下一节将详细讲解运行机制,redux详解 和 react-redux详解