ReactJS高阶之Flux

Flux

单向数据流的架构模式(在 MVC 中属于M层)

╔═════════╗       ╔════════╗       ╔═════════════════╗
║ Actions ║──────>║ Stores ║──────>║ View Components ║
╚═════════╝       ╚════════╝       ╚═════════════════╝
     ^                                      │
     └──────────────────────────────────────┘

基于单向数据流如何更好的管理数据,在 Views 与数据之间进行解耦 ,Store 是关键

  • View 层 Dispatch actions(分发操作)
  • Store 响应action,触发 change事件
  • View 层响应change事件

在flux思想之上,有很多优秀的库,比如:reflux,redux

Reflux

Github | Reflux 介绍 | Sample

下载包:

> npm install reflux --save
  1. Action:定义方法

    var Reflux=require("reflux");
    module.exports=Reflux.createActions([
     "listXxx",
     "getXxx",
     "updateXxx",
     "createXxx",
     ...
    ])
    
  2. Store:监听actions

     //xxx-store.jsx
     var Reflux=require("reflux");
     var Api=require("../utils/api");
     var xxxActions=require("../actions/xxx-action");
    
     module.exports=Reflux.createStore({
         listenables:[XxxActions],        // 监听actions
         data:{},
    
         //添加Action中定义的同名函数
         listXxx:function(){
             return Api.get("...")
                     .then(function(json){
                         if(json && json.success)
                             this.data=json.data;
                         this.trigger(this.data,json);    //触发(监听此Store的Components会接收到)
                     }.bind(this));
         },
         ...
         //也可添加Store自己的函数
     });
    
  3. Component: 调用Action(会触发调用相应的Store方法)或Store方法,监听Store变化

     var Reflux=require("reflux");
     var XxxActions=require("../action/xxx-action");
     var XxxStore=require("../stores/xxx-store");
    
     module.exports=React.createClass({
         //监听Store,捕获到则调用onChange方法(相当于callback)
         mixins:[Reflux.listenTo(XxxStore,"onChange")],
         onChange:function(data,result){
             this.setState({...});
         },
         componentWillMount:function(){
             XxxActions.listXxx();
         },
         ...
     });
    

Redux

中文文档 | Redux 介绍 | 解读redux工作原理

redux (图片来自:http://staltz.com/unidirectional-user-interface-architectures.html)

store 是一个单一对象:

  • store.getState() :获取reducer的root state
  • store.dispatch(action) : 向所有reducer分发action
  • store.subscribe(listener) :注册 state 变化监听器
  • createStore(reducer, [initialState]) 创建Store

下载包:

> npm install redux --save
> npm install react-redux --save

基本使用示例

  1. Component

     var React=require("react");
     var ReactDOM=require("react-dom");
    
     var Counter=React.createClass({
         render:function(){
             return (
               <div>
                 <span>{this.props.value}</span>
                 <button className="btn btn-default" onClick={this.props.onIncreaseClick}>Increase</button>
               </div>
             )
         }
     });
    
  2. Store

     var redux=require("redux");
    
     var countRedux=function(state,action){
         switch(action.type){
             case 'INCREASE':
                 return state+action.step;
         }
         return state;
     }
    
     var store=redux.createStore(countRedux,0);
     store.subscribe(function(){
         console.log(store.getState());
     });
    
  3. Action

     var increaseAction={type:'INCREASE',step:1};
     //test
     store.dispatch(increaseAction);
    
  4. Container

     var Provider=require("react-redux").Provider;
     var connect=require("react-redux").connect;
    
     //Map Redux state to component props
     function mapStateToProps(state){
         return {count:state}
     }
     //Map Redux dispatch to component props
     function mapDispatchToProps(dispatch){
         return {
             onIncreaseClick:function(){dispatch(increaseAction);}
         }
     }
    
     var App=connect(mapStateToProps,mapDispatchToProps)(Counter)
    
     // render
     ReactDOM.render(
       <Provider store={store}>
         <App />
       </Provider>,
       document.getElementById('app')
     );
    

高阶使用示例

redux (图片来自:https://www.udemy.com/react-redux/ 教程)

要点:

  1. Reducer:
     var bookReducer=function(state,action){...};
     var activeBookReducer=function(state,action){...};
     module.exports=combineReducers({
         books:booksReducer,
         activeBook:activeBookReducer
     })
    
  2. Action Creator:

         var selectBook=function(book){
             console.log("Selectd:"+book.title);
             return {type:"BOOK_SELECTED",payload:book};
         }
         module.exports={
             selectBook:selectBook
         }
    
  3. Container:

     var bindActionCreators=require("redux").bindActionCreators;
    
     function mapDispatchToProps(dispatch){
         /*return {
             selectBook:function(book){dispatch({type:'BOOK_SELECTED',payload:book})}
         }*/
         return bindActionCreators(BookActions,dispatch);
     }
    

可使用middleware实现异步Action (例如使用redux-promise

redux (图片来自:https://www.udemy.com/react-redux/ 教程)

要点: 触发action,传递promise对象,使用middleware拦截直到响应,继续传递action到reducers

  1. 下载包redux-promise (Redux Middleware) & axios (Promise Request):

    > npm install redux-promise --save
    > npm install axios --save
    
  2. Action:

     {type:'BOOK_LIST',payload:axios.get(url);}
    
  3. Reducer:

     var booksReducer=function(state,action){
         switch(action.type){
             case 'BOOK_LIST':
                 if(action.payload.status==200){
                     return [action.payload.data];
                 }
         }
         return state;
     }
    
  4. Store

     var createStore=require("redux").createStore;
    
     var applyMiddleware=require("redux").applyMiddleware;
     var ReduxPromise=require("redux-promise");
     var createStoreWithMiddleware=applyMiddleware(ReduxPromise)(createStore);
    
     var store=createStoreWithMiddleware(booksReducers);
    

Reference