草珊瑚的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.htmljavascript

這三篇文章介紹了,
Redux的基本概念和API,異步操做,以及如何跟React相結合。
文章寫得不錯,但實踐起來仍是略顯繁瑣。html

思考

下面提出我本身對Redux結合React使用的思考。java

  1. 使用ramda庫組合自定義中間件。
    這使得代碼更靈活和透明。react

  2. 異步操做也結合ramda庫。
    能夠不用引入第三方redux-thunkredux-promise中間件。
    使用ramda庫更方便組合異步操做。webpack

  3. state以React組件的state爲準。
    redux的state只是輔助計算出React組件的state。web

  4. 往store注入trigger函數,用來setState更新React組件。redux

在線demo
示例代碼以下:
index.htmlpromise

<!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.jsapp

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.jsdom

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

    }
  }
}
相關文章
相關標籤/搜索