redux介绍与入门
一、什么是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' }) // 1 store.dispatch({ type: 'INCREMENT' }) // 2 store.dispatch({ type: 'DECREMENT' }) // 1
可以看到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 );