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 );

 

posted @ 2016-12-16 14:27  水谷  阅读(1813)  评论(0编辑  收藏  举报