参考文章
redux是干嘛用的?
- 状态管理,将页面中state的状态管理起来,统一进行管理
- 页面与数据处理分离,页面只负责根据不同状态显示不通的内容;数据处理交给action、reducer。可以理解为MVC中的MV。
redux中的几个概念:
- action:用于预处理数据,指的是对数据进行加工,然后将数据dispatch给store
- reducer:分类处理数据,对于action中加工好的数据,依据不通的type组织成不通的结果值state,该结果值会同步到store中,然后页面会在store中获取到该值。
- dispatch: 触发动作,可以是在页面中触发action,也可以是在action中改变值后更新页面的操作
- state:组件内部的状态
- store:我的理解为全局唯一的一个容器,是action、reducer和页面三者状态的的协调器。
- type:用于区分操作的标识,常量字符串表示
redux的调用过程:
- 页面view中触发(比如点击按钮),会发送消息到action内,
- action将该消息预处理,区分一下type,然后返回给store,
- store将分好类的消息,分配到reducer中处理state.
- reducer接收到带有type的消息以后,找到对应的处理办法,生成新的state返回给store
- store控制页面渲染
在rn中集成redux:
npm install --save redux
npm install --save react-redux
npm install --save redux-thunk
// 在项目中的导航使用react-navigation, demo中会用到,也配置上
npm install --save react-navigation
npm isntall --save react-native-gesture-handler
npm install --save react-navigation-stack
示例页面
有点丑!

项目目录结构

redux开发,分两个部分
- 项目配置(目录中的root和store),项目中只有一份。
- 页面开发(目录中的actions、constants 、pages和reducers),应用redux的每个页面一份。demo中都是计算页面Counter的,如果有登录页面,那么会相应增加LoginAction、LoginType、LoginReducer和LoginPage。
先说对于每个页面的redux
- CounterPage:所有的页面显示元素都在这里写,同时这个页面通过connect与store绑定。
- CounterAction:数据的预处理过程。
- CounterType:用于区分页面操作的标识字符串常量,都在这里定义。
- CounterReducer:对于不同type处理,组织成最终的state数据。
CounterPage
'use strict'
import {
View, Text, TouchableOpacity,StyleSheet
} from 'react-native';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as counterAction from '../actions/CounterAction';
class CounterPage extends Component {
render() {
const { num, add, subtract } = this.props;
return (
<View style={{alignItems:'center'}}>
<Text style={styles.bodyText}>{num}</Text>
<TouchableOpacity onPress={() => add(num)}>
<Text style={styles.bodyText}>clickme 加1</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => subtract(num)}>
<Text style={styles.bodyText}>clickme 减1</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
bodyText:{
fontSize:30,marginTop:20,backgroundColor:'#dddddd'
}
})
export default connect(
store => ({
num: store.CounterReducer.num,
}),
dispatch => ({
add: (num) => dispatch(counterAction.add(num)),
subtract: (num) => dispatch(counterAction.subtract(num)),
})
)(CounterPage)
重点关注的点:
- 顶部,需要把CounterAction给import进来。
- 底部,需要使用connect,将CounterPage与store建立关系。
- connect的第一个参数:
store => ({
num: store.CounterReducer.num,
}),
在当前props中添加一个字段num,其值为CounterReducer中定义的state中的num字段的值,这样就建立的联系。如果CounterReducer中的num发生了变化,当前的props中的num也会发生变化。
- connect的第二个参数:
dispatch => ({
add: (num) => dispatch(counterAction.add(num)),
subtract: (num) => dispatch(counterAction.subtract(num)),
})
在当前props中添加两个函数add和subtract,其值是调用的CounterAction中的add和subtract。这样页面就可以通过props中的这两个方法来调用action中的方法来,如此即将view和action建立来联系。
- 看render方法
const { num, add, subtract } = this.props;
connect中定义的方法和字段,都是从this.props中获取的。
CounterType
export const COUNTER_ADD = 'COUNTER_ADD'; // 加1标识
export const COUNTER_SUBTRACT = 'COUNTER_SUBTRACT'; // 减1标识
CounterAction
'use strict'
import * as types from '../constants/CounterType';
export function add(value){
const resultValue = value+1;
return dispatch=>{
dispatch(result(types.COUNTER_ADD,resultValue));
}
}
export function subtract(value){
const resultValue = value-1;
return dispatch=>{
dispatch(result(types.COUNTER_SUBTRACT,resultValue));
}
}
function result(type,value){
return {
type:type,
num:value
}
}
需要关注的点:
- import了CounterType,在result方法内将type传入,这样reducer中就可以拿到这个type了。
- 定义了方法add和subtract,在方法内对数据进行了处理,“const resultValue = value-1;”,处理完后,使用dispatch进行触发,将result方法的返回值给了store。记得一定要用dispatch。
CounterReducer
'use strict'
import * as types from '../constants/CounterType';
const initState = {
num: 0
}
export default function CounterReducer(state = initState, action) {
switch (action.type) {
case types.COUNTER_ADD:
return {
...state,
num: action.num
}
case types.COUNTER_SUBTRACT:
return {
...state,
num: action.num
}
default:
return initState;
}
}
需要关注的点:
- initState,一般会定义一个默认值,在“export default function CounterReducer(state = initState, action)”中。
- 参数action内有,CounterAction中传递的参数type和num。
- switch区分不同的type,组织不同的state状态值。
- 在reducers目录中还有个index.js。项目中有多个页面,那么就会有多个XXXReducer,这里的index.js目的是将所有的Reducer都集中起来。
'use strict'
import {
combineReducers
} from 'redux';
import CounterReducer from './CounterReducer';
export default combineReducers({
CounterReducer:CounterReducer,
})
项目中Redux的配置
ConfigueStore.js
'use strict'
import {createStore,applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers/index'
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
export default function configueStore(initState){
return createStoreWithMiddleware(rootReducer,initState);
}
- 创建了store,并与所有reducer建立关联
App.js
这是导航管理中,将所有的页面都集中起来建立关系
'use strict'
import React from 'react';
import CounterPage from '../pages/CounterPage';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
const App = createStackNavigator({
CounterPage: { screen: CounterPage }
// 这里可以增加其他Page
});
export default createAppContainer(App);
Root.js
'use strict'
import React,{Component} from 'react';
import {Provider} from 'react-redux';
import App from './App'
import ConfigueStore from '../store/ConfigueStore';
const store = ConfigueStore();
export default class Root extends Component{
render(){
return (
<Provider store={store}>
<App />
</Provider>
);
}
}
- 创建store,‘const store = ConfigueStore();’
- 引入App,里面是导航管理中的所有页面
- 使用Provider,将store当作属性传入,并包裹App,这样就可以将store与App中的所有页面建立关系。这是redux生效的入口
- 应用入口的index.js,入口要指向Root
import {AppRegistry} from 'react-native';
import Root from './src/root/Root';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => Root);
这样就捋顺了:
应用入口index - Root -(store,reducer,pages三者建立关系)-具体页面内容应用redux
网友评论