文章出處
文章列表
前言
阮大師寫入門教程能力一流。
首推它的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使用的思考。
使用ramda庫組合自定義中間件。
這使得代碼更靈活和透明。異步操作也結合ramda庫。
可以不用引入第三方redux-thunk
,redux-promise
中間件。
使用ramda庫更方便組合異步操作。state以React組件的state為準。
redux的state只是輔助計算出React組件的state。往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();
}
}
}
}
文章列表
全站熱搜