一、什么是flux
1.redux的设计思想与flux是差不多一样的,所以我们先来了解什么flux
2.flux是一种设计模式或者说是框架。以mvc模式来划分的话react是mvc中的view, flux相当于mc,m就是model c就是control。那么我们就明白flux到底是什么了,看下图:
flux包含四个部分 Store、Dispatch、Action、View,其中Store就对应着model,Dispatch、Action就组合成了Control。这么划分仅仅是帮助全局理解flux到底是什么。
3.flux就是一种设计模式,当view或者用户产生一个Action时,Dispatch会解析Action根据不同的Action修改Store,被修改的Store会发消息通知View说:我已经修改了过来取我并更新你自己吧。
4.一个简单例子
// store
var Store = {
state:{
loginData:{
type:'login',
data:'no login',
},
logoutData:{
type:'logout',
data:'',
}
},
login:function(data){
this.state.loginData = data;
},
logout:function(data){
this.state.logoutData = data;
},
getState:function(){
return this.state;
},
sendEvent:function(){
this.callback();
},
addChangeListener: function(callback) {
this.callback = callback;
},
removeChangeListener: function(callback) {
}
} // Dispatch
var Dispatcher = require('flux').Dispatcher;
var dispatch = new Dispatcher();
dispatch.register(function(payload){
switch (payload.type){
case 'login' :
Store.login(payload);
Store.sendEvent();
break;
case 'logout':
Store.logout(payload);
Store.sendEvent();
break;
}
}); // View
Store.addChangeListener(()=>{
console.log('{\nloginData:{type:'+Store.getState().loginData.type + ' data:' + Store.getState().loginData.data+ '}');
console.log('logoutData:{type:'+Store.getState().logoutData.type + ' data:' + Store.getState().logoutData.data+ '}\n}');
}); // Action
var loginAction = {
type: 'login',
data: 'login sucessed'
};
var logoutAction = {
type: 'logout',
data: 'logout sucessed'
};
console.log('登录....');
dispatch.dispatch(loginAction);
console.log('退出....');
dispatch.dispatch(logoutAction);
二、redux
1.我们先看看看官网的一个例子
var Redux = require('redux')
var createStore = Redux.createStore function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// 创建store
let store = createStore(counter)
store.subscribe(() =>
console.log(store.getState())
) store.dispatch({ type: 'INCREMENT' })
//
store.dispatch({ type: 'INCREMENT' })
//
store.dispatch({ type: 'DECREMENT' })
//
可以看到redux与flux原理是一样的,只是实现不一样。
1.redux把dispatch封装到了Store里
// 所以我们可以直接通过store来发送dispatch
store.dispatch({ type: 'INCREMENT' })
2.抽象出一个reducer概念(counter就是一个reducer),reducer就是一个[根据不同的dispatch处理并生产新的state的一个程序]。
// 处理自增、或者自减的程序
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
要理解redux,其实就是要理解Redux提供的Store与reducer。
三、react中使用redux
我们将会重头创建一个React-native项目,然后加入redux框架
#初始化一个react-native项目
$ react-native init reduxTest
$ cd reduxTest/ios
$ open reduxTest.xcodeproj
#这样就创建并打开了一个iOS的react-native项目
1.添加app.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight
} from 'react-native';
class App extends Component {
onPress(){ }
render() {
let welcome = this.props.appInfo?this.props.appInfo.welcome:'Welcome to Redux test!'
return (
<View style={styles.container}>
<Text style={styles.welcome}>
{welcome}
</Text>
<TouchableHighlight onPress={this.onPress.bind(this)}>
<Text >
Click me!
</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
module.exports = App;
2.修改reduxTest/index.ios.js
import React, { Component } from 'react';
import {
AppRegistry,
} from 'react-native';
import App from './app'
export default class reduxTest extends Component {
render() {
return (
<App></App>
);
}
}
AppRegistry.registerComponent('reduxTest', () => reduxTest);
这时候我们得到一个简单的测试app,下面我通过redux来管理app组件的state(redux把state映射到props)。
效果:当点击Click me! 按钮时,会吧welcome信息改为 have clicked!
具体流程就是:
(1).点击 Click me! 按钮 ,会通过redux的Store发送一个dispatch给reducer,reducer把welcome改为‘have clicked’
(2).然后redux会通知app 组件重新渲染
3.安装redux、react-redux、redux-thunk
$ npm install redux --save
$ npm install react-redux --save
$ npm install redux-thunk --save
3.直接上源码,代码后面有解释
总共涉及4个文件,需要重点关注的代码将会被标红。
- index.ios.js -- 创建store
- app.js -- 根据store的改变做出相应的处理、用户点击时发出action
- reducer.js -- 处理action
- action.js -- 具体的action
index.ios.js:
import React, { Component } from 'react';
import {
AppRegistry,
} from 'react-native'; import App from './app'
import appReducer from './reducer' import {createStore,
applyMiddleware} from 'redux';
import {Provider} from 'react-redux';
import thunk from 'redux-thunk'; let store = createStore(appReducer,
applyMiddleware(thunk) // 用于异步的action
); export default class reduxTest extends Component {
render() {
return (
<Provider store={store}>
<App></App>
</Provider> );
}
}
AppRegistry.registerComponent('reduxTest', () => reduxTest);
解析
这里引入了四个redux相关组件
- createStore --- 是一个函数,用于创建store
- applyMiddleware --- 是一个函数,用于使用中间件
- hunk --- 是一个函数,是中间件用于使action函数支持异步;
- Provider --- 是一个react组件,主要提供一个全局的store使得它的子组件都能访问到
创建store的代码:
let store = createStore(
appReducer,
applyMiddleware(thunk) // 用于异步的action
);
/**
@appReducer :是一个reducer,我们说过是用于处理action的。
@applyMiddleware(thunk) : 应用一个叫thunk的中间件,任何一个action执行前会先执行thunk 这里我们应该记住:
store提供一个保存state的地方
store也提供了一个发出dispatch的方法
store也提供了一个监听state的方法
*/
Provider:Provider是提供者,意思就是给他的子组件提供一个store,这个store就是我们上面创建的。
app.js:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight
} from 'react-native';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux'; import WelcomeAction from './action' class App extends Component {
// 定义 上线文里store属性的类型为object
static contextTypes = {
store: React.PropTypes.object
}
componentDidMount() {
// store的作用1: 监听state的变化
const { store } = this.context;
store.subscribe(
()=>{
// store的作用2: 获取state
let state = store.getState();
// state改变了
console.log('state:',state);
}
);
}
onPress(){
// 1.直接用store发生dipatch
let action = {
type:'welcome',
data:{
text:'have clicked from app.js',
}
}
// store的作用3: 发送dispatch
this.context.store.dispatch(action) // this.props.onPressAction()
}
render() {
let welcome = this.props.welcome
return (
<View style={styles.container}>
<Text style={styles.welcome}>
{welcome}
</Text>
<TouchableHighlight onPress={this.onPress.bind(this)}>
<Text >
Click me!
</Text>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});
function mapStateToProps(state) {
return {
welcome: state.welcome
}
}
function mapDispatchToProps(dispatch) {
return {
onPressAction:bindActionCreators(WelcomeAction,dispatch),
}
}
module.exports = connect(mapStateToProps,mapDispatchToProps)(App);
解析
(1)获取store
因为app组件是Provider组件的子组件,所以app组件跟Provider组件是共享一个context(上下文)的 --- 这个是react的规定,不了解的请自行补相应知识。
只要在app组件定义一下store的类型就能使用了
// 定义 上线文里store属性的类型为object
static contextTypes = {
store: React.PropTypes.object
}
// 通过下面就能获取到store
this.context.store
这个store是与创建Provider时传入的store是同一个
<Provider store={store}>
<App></App>
</Provider>
(2)使用store
获取到store之后我们就可以用于发送dispatch、监听state了
发送dispatch:
let action = {
type:'welcome',
data:{
text:'have clicked from app.js',
}
}
// store的作用3: 发送dispatch
this.context.store.dispatch(action)
action参数是一个对象,对象结构没有做要求。
action被dispatch之后会被reducer处理,处理完后就会发一个通知说state已经更新了。
通过下面代码来监听通知
// store的作用1: 监听state的变化
const { store } = this.context;
store.subscribe(
()=>{
// store的作用2: 获取state
let state = store.getState();
// state改变了
// 根据state做相应的渲染
console.log('state:',state);
}
);
我看下面的reducer是怎么处理action的
reducer.js:
function Reducer(state = {welcome:'Welcome to Redux test!'}, action) {
switch (action.type) {
case 'welcome':
return {welcome:action.data.text};
default:
return state
}
}
module.exports = Reducer;
解析:
很简单的处理,如果action的type等于‘welcome’的话,就直接返回一个对象{welcome:action.data.text};
监听者收到的就是这个返回的对象。
值得注意,Reducer的参数 state = {welcome:'Welcome to Redux test!'},是state的默认值
---------------------------------------------------------------------
每次都通过this.context.sotre来dispatch、subscribe,大家都觉得很烦,好吧redux已经做了封装:
引入两个组件:
- connect ---- 用于封装App组件
- bindActionCreators --- 绑定action的构造者
具体使用:
function mapStateToProps(state) {
return {
welcome: state.welcome
}
}
function mapDispatchToProps(dispatch) {
return {
onPressAction:bindActionCreators(WelcomeAction,dispatch),
}
} module.exports = connect(mapStateToProps,mapDispatchToProps)(App);
解析:
function mapStateToProps(state)
正如函数名所表示,它的作用就是把state映射到props上。这里的state是指store保存的state,props是指app组件的props。
这个函数需要返回一个对象
return {
welcome: state.welcome
}
然后通过connect组件封装一下
module.exports = connect(mapStateToProps,mapDispatchToProps)(App);
这样子,在app组件内部就能通过this.props.welcome来获取store保存的state对应的welcome的值了,是不是分方便?
既然state能映射到props,那么dispatch action也能映射
import WelcomeAction from './action'
function mapDispatchToProps(dispatch) {
return {
onPressAction:bindActionCreators(WelcomeAction,dispatch),
}
}
module.exports = connect(mapStateToProps,mapDispatchToProps)(App);
上面的代码意思就是吧dispatch映射到props上,dispatch是sotre的dispatch,props是app的props.
我们可以这样直接发出一个action,
this.props.onPressAction()
onPressaction()等同于 WelcomeAction
WelcomeAction是什么请看往下看:
action.js:
function WelcomeAction () {
// 异步
return (dipatch, getState) => {
return new Promise((resolve,reject)=>{
setTimeout(()=>{
let action = {
type:'welcome',
data:{
text:'have clicked??',
}
}
dipatch(action);
resolve();
},2000);
});
}
// 同步
// return {
// type:'welcome',
// data:{
// text:'have clicked',
// }
// }
}
module.exports = WelcomeAction
WelcomeAction函数用到dispatch等于store.dispatch
这样做的目的是把action独立出来方便单独管理。
action函数,如果需要异步执行就返回一个Promise,同步执行可以直接返回一个新的state
值得注意如果action函数需要异步执行,在创建store的时候必须使用中间件trunk
import thunk from 'redux-thunk';
let store = createStore(
appReducer,
applyMiddleware(thunk) // 用于异步的action
);