本文的讀者為了解flux概念,熟悉react,了解es6語法的同學
redux
是最近很火的一個 flux
框架,短短的一個月現在已經有2900+的 star 了,watch之后每天收到幾百封 pr 的郵件,廢話就不多說了。
為什么要用 redux
,請看連接 The Evolution of Flux Frameworks。
主要特點
- Everything (Stores, Action Creators, configuration) is hot reloadable. —— 配合起 hotloader 應該特別爽,雖然現在用的是
browserify
,好處感覺并不明顯。 -
store
中的數據不受限制,可以是number
object
array
等等,廢話,因為它的store
只是簡單的函數。 - 提供
devtools
,監控action
的觸發以及state
的變化。 - 源碼清晰簡單,輕量級,根本不需要文檔,直接看源碼就行了....缺點就是如果看不懂源碼,光看文檔會覺得不夠清晰。
-
api
很精簡,不用記茫茫多的api
- every thing is simple function
-
connecter
與provider
這兩個東西用起來總覺得很繁瑣,不那么優雅。
下面通過寫的一個簡單counter的例子 介紹 redux
的核心方法以及一些需要注意的地方。
- 同步與異步兩種actionCreator
- middleware的使用
- dispatch actions
- 拿到并同步state
代碼放在https://github.com/yeatszhang/redux-tutorial, 需要安裝gulp
代碼是基于分支 v1.0.0-rc
api略微有些區別,詳情見 Breaking API changes for 1.0。
actionCreator
actions creator 是用來生成 action 的函數,在默認情況下可以接受返回object
或者function
的函數,很多人學習flux的時候把action與actionCreator弄混淆....:
// 直接返回object
actionCreators.addTodo = function(text) {
return {
type: types.ADD_TODO,
text
};
}
// 返回函數
actionCreators.incrementAsync = function() {
return (dispatch, getState) => {
// actionCreator中可以通過getState獲得當前的state
console.log(getState());
// 異步action
setTimeout(() => {
dispatch({
type: INCREMENT_COUNTER2,
});
}, 1000);
};
};
在沒有使用任何 middleware
的情況下,只有這有兩種 action
可以被 dispatch
。
app
在動態內容的最外層應該使用Provider進行包裹,provider接收store作為參數,注意children是一個函數
并不是reactElement
。
provider
將store作為context往子節點進行傳遞,并實現store
的熱替換。因此在provider內的組件其實可以不通過connect來拿到dispatch以及state,而直接通過context拿到store對象,不過作者不推薦這么做。
import React from 'react';
import { createStore, applyMiddleware, combineReducers } from 'redux';
// redux midlleware repositories
import thunk from 'redux-thunk';
// 將 redux 與 react 相關的部分,如 connector provider 單獨抽取出來
import { Provider } from 'react-redux';
import reducers from '../reducers';
import CounterApp from './CounterApp.js';
import logMiddleware from '../middleWares/logMiddleware.js';
const reducer = combineReducers(reducers);
const createStoreWithMiddleware = applyMiddleware(thunk, logMiddleware)(createStore);
const store = createStoreWithMiddleware(reducer);
// 使用middleWare thunk, 如果沒有自定義中間層的需求可以直接寫
// const store = createStore(reducer);
class App extends React.Component {
render() {
return (
<Provider store={store}>
{() => <CounterApp />}
</Provider>
);
}
}
smart component
smart component擁有兩個特點:
- 自動關聯store中的state并自動re-render
- 可以通過dispatch來分發事件,從而觸發store的更新
剛接觸redux的同學肯定會覺得這個connect很難以理解。還是在代碼里面說把。。。
/**
* Created by yichizhang on 15/7/26.
*/
import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { Connector } from 'react-redux';
import Counter from '../components/Counter';
import actionCreators1 from '../actionCreators/actionCreators1.js';
import actionCreators2 from '../actionCreators/actionCreators2.js';
// state 是各reducer中state的集合
function select(state) {
// 從各reducer中挑選出component需要監聽的state
return {
counter1: state.reducer1.counter,
counter2: state.reducer2.counter,
};
}
export default class CounterApp extends Component {
// select函數的返回值會與dispatch組裝程一個object作為參數
// 從這里看出connector就是幫忙拿到provider中store的dispatch方法以及挑選出需要使用的state
renderChild({ counter1, counter2, dispatch}) {
// 個人覺得這樣使用action十分不方便,尤其是當組件只需要觸發actions不需要監聽store的變化的時候。我會偷懶通過context去拿到dispatch~~
const actions1 = bindActionCreators(actionCreators1, dispatch);
const actions2 = bindActionCreators(actionCreators2, dispatch);
const props = { ...actions1, ...actions2, counter1, counter2 };
// 所有的action以及state都會以props的形式提供給Counter,然后在Counter里面就可以為所欲為了~
return <Counter {...props} />;
}
render() {
return (
<Connector select={select}>
{this.renderChild}
</Connector>
);
}
}
reducer
redux認為程序員不需要去寫store中的邏輯而只需要寫明對state的處理邏輯就好:
old sate => action => new state
這是一個完全同步的過程。reducer只需要聲明初始狀態以及state在接收到action之后的改變規則就可以了。
import React from 'react/addons';
import {INCREMENT_COUNTER1, DECREMENT_COUNTER1} from '../constants/actionsTypes.js';
const update = React.addons.update;
// state可以是任何類型
const initialState = {
counter: 0,
};
// reducer只是一個簡單的switch方法
export default function counter(state = initialState, action = null) {
switch (action.type) {
case INCREMENT_COUNTER1:
// 需要注意的是connector當select中的state發生變化時會做一個shallow equal的操作,
// 所以如果需要操作引用值的時候一定不能直接賦值,需要使用addon中的update或者immutable.js,知道看到這兩個工具又不想繼續學了..其實很簡單
// 這樣可以大大避免重復的render,從而提高性能
return update(state, {
counter: {
$set: state.counter + 1,
},
});
case DECREMENT_COUNTER1:
return update(state, {
counter: {
$set: state.counter - 1,
},
});
default:
return state;
}
}
middleWare
感興趣的同學可以看看,一般來說默認的thunk就夠用了。我在例子里加了個log的中間層
// 打印觸發的action
function logMiddleware() {
// 這里的next是下一個middleWare
return function(next) {
return function(action) {
// 打印此action并使用下一個middleWare處理該action
console.log(action);
next(action);
};
};
}
export default logMiddleware;
// 下面是默認的thunk middleWare
function thunkMiddleware(_ref) {
var dispatch = _ref.dispatch;
var getState = _ref.getState;
return function (next) {
return function (action) {
// 如果是函數則將dispatch與getState作為參數執行函數,否則交給寫一個middleware處理
return typeof action === 'function' ? action(dispatch, getState) : next(action);
};
};
}
結語
其實redux不明白的地方直接看源碼更好,redux的代碼量很小而且組織也很清晰,建議大家都去看,不過作者貌似函數式編程的思維很重,大量使用修飾器的語法,還有reduce~ 挺繞的~
之后會總結自己閱讀redux源碼的一些心得,以及各功能模塊的實現原理~