1.redux的設計思想與flux是差很少同樣的,因此咱們先來了解什麼fluxreact
2.flux是一種設計模式或者說是框架。以mvc模式來劃分的話react是mvc中的view, flux至關於mc,m就是model c就是control。那麼咱們就明白flux究竟是什麼了,看下圖: ios
flux包含四個部分 Store、Dispatch、Action、View,其中Store就對應着model,Dispatch、Action就組合成了Control。這麼劃分僅僅是幫助全局理解flux究竟是什麼。web
3.flux就是一種設計模式,當view或者用戶產生一個Action時,Dispatch會解析Action根據不一樣的Action修改Store,被修改的Store會發消息通知View說:我已經修改了過來取我並更新你本身吧。npm
4.一個簡單例子redux
// 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);
1.咱們先看看看官網的一個例子react-native
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裏xcode
// 因此咱們能夠直接經過store來發送dispatch store.dispatch({ type: 'INCREMENT' })
2.抽象出一個reducer概念(counter就是一個reducer),reducer就是一個[根據不一樣的dispatch處理並生產新的state的一個程序]。mvc
// 處理自增、或者自減的程序 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。 app
咱們將會重頭建立一個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:
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相關組件
建立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已經作了封裝:
引入兩個組件:
具體使用:
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 );