阮大師寫入門教程能力一流。
首推它的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
使用ramda庫組合自定義中間件。
這使得代碼更靈活和透明。react
異步操做也結合ramda庫。
能夠不用引入第三方redux-thunk
,redux-promise
中間件。
使用ramda庫更方便組合異步操做。webpack
state以React組件的state爲準。
redux的state只是輔助計算出React組件的state。web
往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(); } } } }