草珊瑚的redux使用方式

前言

阮大师写入门教程能力一流。
首推它的Redux三篇入门文章。
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html

这三篇文章介绍了,
Redux的基本概念和API,异步操作,以及如何跟React相结合。
文章写得不错,但实践起来还是略显繁琐。

思考

下面提出我自己对Redux结合React使用的思考。

  1. 使用ramda库组合自定义中间件。
    这使得代码更灵活和透明。

  2. 异步操作也结合ramda库。
    可以不用引入第三方redux-thunkredux-promise中间件。
    使用ramda库更方便组合异步操作。

  3. state以React组件的state为准。
    redux的state只是辅助计算出React组件的state。

  4. 往store注入trigger函数,用来setState更新React组件。

在线demo
示例代码如下:
index.html

<!doctype html>
<html>
  <head>
    <meta charset="utf-8"/>
  </head>
  <body>
    <h1>Hello world</h1>
    <div id="app"></div>  
    <script src="index.js"></script>
  </body>
    
</html>

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import store from './store.js';
import {connect} from './connect.js';
class Test extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value:0,
      otherNum:0,
      asyncing:false
    };
  }
  dispatch(obj){
    obj.state= this.state;
    store.dispatch(obj);
  }
  setOtherData(){
    this.dispatch({ type: 'Other' });
  }
  setData(){
    this.dispatch({ type: 'INCREMENT' });
  }
  async(){
    this.dispatch({type: 'Loading' });
    this.dispatch({ type: 'ASYNC'});
  }
  componentDidMount(){
    //store.dispatch({type: 'Init'})    
  }
  render() {
    const t = this;
    console.log('render', store.getState(), t.state);
    const {value ,asyncing,otherNum } = t.state;
    return (
      <div>
        <div>数字:{value}</div>
        <div onClick={t.setData.bind(this)}>点我+1</div>
        {
          (()=>{
            if(asyncing){
              return (<div>异步加载中</div>);
            }
            else{
              return (<div onClick={t.async.bind(t)}>点我异步+10</div>);
            }
          })()
        }
        <br />
          <div onClick={t.setOtherData.bind(this)}>点我其他数字+1</div>
          <div>其他数字:{otherNum}</div>

     </div>);
  }
}
const NewTest  = connect(store)(Test) ;

ReactDOM.render(
<NewTest />,
document.getElementById('app') 
)


module.exports = NewTest;

store.js

import { createStore } from 'redux';
import R from 'ramda';

const isPromise = function(e){
  return !!e&&typeof e.then=="function";
};

const setTimeout1 = R.curry(function(  state ){
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve(state+ 10);
    },1000);
  })
});

// reducer,仅用于计算。
function counter(state = {}, action) {
  if(isPromise(state)){
      state = {};
   }
  state = R.merge(state, action.state);
  console.log('reducer中的state',state);
  switch (action.type) {
  case 'Init':
    return action.state;    
  case 'INCREMENT':
    state.value = state.value + 1;
    return state
  case 'ASYNC':
    return R.composeP( setTimeout1)(state.value);
  case 'Loading':
    return {asyncing: true} 
   case 'Other':
     return {otherNum: state.otherNum+1};
  default:
    return state
  }
}


let store = createStore(counter);

// subscribe,可用于执行含有副作用的操作。
store.subscribe((e) =>{
  console.log('subscribe',store, store.getState(),e, this);
  let state = store.getState();
  if(isPromise(state)){
    state.then(function(num){
      store.trigger({value: num, asyncing:false });
    })
  }
  else{
     store.trigger(store.getState());
  }

})

module.exports = store;

connect.js

import {type} from 'ramda';

exports.connect = function (listenable, context) {
  if(type(listenable) !== 'Object'){
    throw new Error('connect function\'s argument is not a object');
  }
  listenable.trigger = function (obj,fn) {
    this.setState(obj || {}, fn);
  };
  listenable.getReactState = function(){
    return this.state;
  };
  return function(otherReactClass){
    return class baseReactClass extends otherReactClass {
      constructor(){
        super();
      }
      componentDidMount(...args) {
        context = context || this;
        listenable.trigger = listenable.trigger.bind(context);
        listenable.getReactState = listenable.getReactState.bind(context);
        super.componentDidMount();    
      }
      componentWillUnmount() {
        listenable.trigger = null;
        super.componentWillUnmount();
      }

    }
  }
}
posted @ 2017-06-01 16:46  草珊瑚  阅读(280)  评论(0编辑  收藏  举报