摘要:寫法一,返回值是一個(gè)對(duì)象。我們已經(jīng)知道,中間件只關(guān)注函數(shù)的傳遞,而且也不關(guān)心函數(shù)的返回值,所以只需要讓認(rèn)識(shí)這個(gè)函數(shù)就可以了。
react的FLUX數(shù)據(jù)流一直搞不清楚,他不像Angular的雙向數(shù)據(jù)綁定,做一個(gè)model獲取數(shù)據(jù),然后通過controller來管理view上的數(shù)據(jù)顯示就可以了。單項(xiàng)數(shù)據(jù)流引入了太多的概念,state、action、reducer、dispatch。就算看的懂圖,也不一定能coding出來。
不過我總算先搞定了Redux。
store
reducer
action
dispatch
connect
router
middleware
thunk
Basic Usage 1st 實(shí)現(xiàn)action方法export const addDeck = name => ({ type: "ADD_DECK", data: name });2nd 根據(jù)action方法創(chuàng)建reducer方法
export const showBack = (state, action) => { switch(action.type) { case "SHOW_BACK": return action.data || false; default: return state || false; } };3rd 根據(jù)reducer方法創(chuàng)建store
const store = createStore(combineReducers(reducers));
store.subscribe()方法設(shè)置監(jiān)聽函數(shù),一旦 State 發(fā)生變化,就自動(dòng)執(zhí)行這個(gè)函數(shù)。
store.subscribe(listener);
顯然,只要把 View 的更新函數(shù)(對(duì)于 React 項(xiàng)目,就是組件的render方法或setState方法)放入listen,就會(huì)實(shí)現(xiàn) View 的自動(dòng)渲染。
store.subscribe方法返回一個(gè)函數(shù),調(diào)用這個(gè)函數(shù)就可以解除監(jiān)聽。
let unsubscribe = store.subscribe(() => console.log(store.getState()) ); unsubscribe();4th 引入react-redux的
5th react組件中通過connect方法綁定store和dispatch。{...}
const mapStateToProps = (newTalks) => ({ newTalks }); const mapDispatchToProps = dispatch => ({ testFunc: () => dispatch(updataTalkLists(1)), receiveData: () => dispatch(receiveData()) }); export default connect(mapStateToProps, mapDispatchToProps)(MainPage);6th this.props中直接調(diào)用action方法。
this.props.receiveDataWith react-router
結(jié)合router使用時(shí)需要有2步。
1st 綁定routing到reducer上import { syncHistoryWithStore, routerReducer } from "react-router-redux"; import * as reducers from "./redux/reducer"; reducers.routing = routerReducer; const store = createStore(combineReducers(reducers));2nd 使用syncHistoryWithStore綁定store和browserHistory
const history = syncHistoryWithStore(browserHistory, store);Async{routes}
類似 Express 或 Koa 框架中的中間件。它提供的是位于 action 被發(fā)起之后,到達(dá) reducer 之前的擴(kuò)展。
中間件的設(shè)計(jì)使用了非常多的函數(shù)式編程的思想,包括:高階函數(shù),復(fù)合函數(shù),柯里化和ES6語法,源碼僅僅20行左右。
項(xiàng)目中主要使用了三個(gè)中間件,分別解決不同的問題。
thunkMiddleware:處理異步Action
apiMiddleware:統(tǒng)一處理API請(qǐng)求。一般情況下,每個(gè) API 請(qǐng)求都至少需要 dispatch 三個(gè)不同的 action(請(qǐng)求前、請(qǐng)求成功、請(qǐng)求失敗),通過這個(gè)中間件可以很方便處理。
loggerMiddleware:開發(fā)環(huán)境調(diào)試使用,控制臺(tái)輸出應(yīng)用state日志
實(shí)現(xiàn)action異步操作,必須要引入middleware。我這里用了applyMiddleware(thunkMiddleware)組件,也可以用其他的。
1st 創(chuàng)建store是引入Middlewareimport thunkMiddleware from "redux-thunk"; import { createStore, combineReducers, applyMiddleware } from "redux"; const store = createStore(combineReducers(reducers), applyMiddleware(thunkMiddleware));2nd 創(chuàng)建一個(gè)可以執(zhí)行dispacth的action
這也是中間件的作用所在。
export const receiveData = data => ({ type: "RECEIVE_DATA", data: data }); export const fetchData = () => { return dispatch => { fetch("/api/data") .then(res => res.json()) .then(json => dispatch(receiveData(json))); }; };3rd 組件中對(duì)異步的store元素有相應(yīng)的判斷操作。
React組件會(huì)在store值發(fā)生變化時(shí)自動(dòng)調(diào)用render()方法,更新異步數(shù)據(jù)。但是我們同樣也需要處理異步數(shù)據(jù)沒有返回或者請(qǐng)求失敗的情況。否則渲染會(huì)失敗,頁(yè)面卡住。
if(!data.newTalks) { return(); }相關(guān)知識(shí) Store的實(shí)現(xiàn)
Store提供了3個(gè)方法
import { createStore } from "redux"; let { subscribe, //監(jiān)聽store變化 dispatch, //調(diào)用action方法 getState //返回當(dāng)前store } = createStore(reducer);
下面是create方法的一個(gè)簡(jiǎn)單實(shí)現(xiàn)
const createStore = (reducer) => { let state; let listeners = []; const getState = () => state; const dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); }; const subscribe = (listener) => { listeners.push(listener); return () => { listeners = listeners.filter(l => l !== listener); } }; dispatch({}); return { getState, dispatch, subscribe }; };combineReducer的簡(jiǎn)單實(shí)現(xiàn)
const combineReducers = reducers => { return (state = {}, action) => { return Object.keys(reducers).reduce( (nextState, key) => { nextState[key] = reducers[key](state[key], action); return nextState; }, {} ); }; };中間件
createStore方法可以接受整個(gè)應(yīng)用的初始狀態(tài)作為參數(shù),那樣的話,applyMiddleware就是第三個(gè)參數(shù)了。
中間件的次序有講究,logger就一定要放在最后,否則輸出結(jié)果會(huì)不正確。
const store = createStore( reducer, initial_state, applyMiddleware(thunk, promise, logger) );
applyMiddlewares的實(shí)現(xiàn),它是將所有中間件組成一個(gè)數(shù)組,依次執(zhí)行
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer); var dispatch = store.dispatch; var chain = []; var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }; chain = middlewares.map(middleware => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch); return {...store, dispatch} } }
上面代碼中,所有中間件被處理后得到一個(gè)數(shù)組保存在chain中。之后將chain傳給compose,并將store.dispatch傳給返回的函數(shù)。??梢钥吹剑虚g件內(nèi)部(middlewareAPI)可以拿到getState和dispatch這兩個(gè)方法。
那么在這里面做了什么呢?我們?cè)倏碿ompose的實(shí)現(xiàn):
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } else { const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) } }
compose中的核心動(dòng)作就是將傳進(jìn)來的所有函數(shù)倒序(reduceRight)進(jìn)行如下處理:
(composed, f) => f(composed)
我們知道Array.prototype.reduceRight是從右向左累計(jì)計(jì)算的,會(huì)將上一次的計(jì)算結(jié)果作為本次計(jì)算的輸入。再看看applyMiddleware中的調(diào)用代碼:
dispatch = compose(...chain)(store.dispatch)
compose函數(shù)最終返回的函數(shù)被作為了dispatch函數(shù),結(jié)合官方文檔和代碼,不難得出,中間件的定義形式為:
function middleware({dispatch, getState}) { return function (next) { return function (action) { return next(action); } } } 或 middleware = (dispatch, getState) => next => action => { next(action); }
也就是說,redux的中間件是一個(gè)函數(shù),該函數(shù)接收dispatch和getState作為參數(shù),返回一個(gè)以dispatch為參數(shù)的函數(shù),這個(gè)函數(shù)的返回值是接收action為參數(shù)的函數(shù)(可以看做另一個(gè)dispatch函數(shù))。在中間件鏈中,以dispatch為參數(shù)的函數(shù)的返回值將作為下一個(gè)中間件(準(zhǔn)確的說應(yīng)該是返回值)的參數(shù),下一個(gè)中間件將它的返回值接著往下一個(gè)中間件傳遞,最終實(shí)現(xiàn)了store.dispatch在中間件間的傳遞。
redux-promise中間件既然 Action Creator 可以返回函數(shù),當(dāng)然也可以返回其他值。另一種異步操作的解決方案,就是讓 Action Creator 返回一個(gè) Promise 對(duì)象。
寫法一,返回值是一個(gè) Promise 對(duì)象。
const fetchPosts = (dispatch, postTitle) => new Promise(function (resolve, reject) { dispatch(requestPosts(postTitle)); return fetch(`/some/API/${postTitle}.json`) .then(response => { type: "FETCH_POSTS", payload: response.json() }); });
寫法二,Action 對(duì)象的payload屬性是一個(gè) Promise 對(duì)象。這需要從redux-actions模塊引入createAction方法,并且寫法也要變成下面這樣。
import { createAction } from "redux-actions"; class AsyncApp extends Component { componentDidMount() { const { dispatch, selectedPost } = this.props // 發(fā)出同步 Action dispatch(requestPosts(selectedPost)); // 發(fā)出異步 Action dispatch(createAction( "FETCH_POSTS", fetch(`/some/API/${postTitle}.json`) .then(response => response.json()) )); }
上面代碼中,第二個(gè)dispatch方法發(fā)出的是異步 Action,只有等到操作結(jié)束,這個(gè) Action 才會(huì)實(shí)際發(fā)出。注意,createAction的第二個(gè)參數(shù)必須是一個(gè) Promise 對(duì)象。
redux-promise的源碼
export default function promiseMiddleware({ dispatch }) { return next => action => { if (!isFSA(action)) { return isPromise(action) ? action.then(dispatch) : next(action); } return isPromise(action.payload) ? action.payload.then( result => dispatch({ ...action, payload: result }), error => { dispatch({ ...action, payload: error, error: true }); return Promise.reject(error); } ) : next(action); }; }
從上面代碼可以看出,如果 Action 本身是一個(gè) Promise,它 resolve 以后的值應(yīng)該是一個(gè) Action 對(duì)象,會(huì)被dispatch方法送出(action.then(dispatch)),但 reject 以后不會(huì)有任何動(dòng)作;如果 Action 對(duì)象的payload屬性是一個(gè) Promise 對(duì)象,那么無論 resolve 和 reject,dispatch方法都會(huì)發(fā)出 Action。
mapStateToProps()mapStateToProps是一個(gè)函數(shù)。它的作用就是像它的名字那樣,建立一個(gè)從(外部的)state對(duì)象到(UI 組件的)props對(duì)象的映射關(guān)系
mapStateToProps會(huì)訂閱 Store,每當(dāng)state更新的時(shí)候,就會(huì)自動(dòng)執(zhí)行,重新計(jì)算 UI 組件的參數(shù),從而觸發(fā) UI 組件的重新渲染。
mapStateToProps的第一個(gè)參數(shù)總是state對(duì)象,還可以使用第二個(gè)參數(shù),代表容器組件的props對(duì)象。
使用ownProps作為參數(shù)后,如果容器組件的參數(shù)發(fā)生變化,也會(huì)引發(fā) UI 組件重新渲染。
connect方法可以省略mapStateToProps參數(shù),那樣的話,UI 組件就不會(huì)訂閱Store,就是說 Store 的更新不會(huì)引起 UI 組件的更新。
// 容器組件的代碼 //mapDispatchToProps()// All // const mapStateToProps = (state, ownProps) => { return { active: ownProps.filter === state.visibilityFilter } }
mapDispatchToProps是connect函數(shù)的第二個(gè)參數(shù),用來建立 UI 組件的參數(shù)到store.dispatch方法的映射。也就是說,它定義了哪些用戶的操作應(yīng)該當(dāng)作 Action,傳給 Store。它可以是一個(gè)函數(shù),也可以是一個(gè)對(duì)象。
mapDispatchToProps作為函數(shù),應(yīng)該返回一個(gè)對(duì)象,該對(duì)象的每個(gè)鍵值對(duì)都是一個(gè)映射,定義了 UI 組件的參數(shù)怎樣發(fā)出 Action。
const mapDispatchToProps = ( dispatch, ownProps ) => { return { onClick: () => { dispatch({ type: "SET_VISIBILITY_FILTER", filter: ownProps.filter }); } }; }
如果mapDispatchToProps是一個(gè)對(duì)象,它的每個(gè)鍵名也是對(duì)應(yīng) UI 組件的同名參數(shù),鍵值應(yīng)該是一個(gè)函數(shù),會(huì)被當(dāng)作 Action creator ,返回的 Action 會(huì)由 Redux 自動(dòng)發(fā)出。
const mapDispatchToProps = { onClick: (filter) => { type: "SET_VISIBILITY_FILTER", filter: filter }; }
React-Redux 提供Provider組件,可以讓容器組件拿到state,它的原理是React組件的context屬性,請(qǐng)看源碼。
class Provider extends Component { getChildContext() { return { store: this.props.store }; } render() { return this.props.children; } } Provider.childContextTypes = { store: React.PropTypes.object }
上面代碼中,store放在了上下文對(duì)象context上面。然后,子組件就可以從context拿到store,代碼大致如下。
class VisibleTodoList extends Component { componentDidMount() { const { store } = this.context; this.unsubscribe = store.subscribe(() => this.forceUpdate() ); } render() { const props = this.props; const { store } = this.context; const state = store.getState(); // ... } } VisibleTodoList.contextTypes = { store: React.PropTypes.object }redux-thunk
我們知道,異步調(diào)用什么時(shí)候返回前端是無法控制的。對(duì)于redux這條嚴(yán)密的數(shù)據(jù)流來說,如何才能做到異步呢。redux-thunk的基本思想就是通過函數(shù)來封裝異步請(qǐng)求,也就是說在actionCreater中返回一個(gè)函數(shù),在這個(gè)函數(shù)中進(jìn)行異步調(diào)用。我們已經(jīng)知道,redux中間件只關(guān)注dispatch函數(shù)的傳遞,而且redux也不關(guān)心dispatch函數(shù)的返回值,所以只需要讓redux認(rèn)識(shí)這個(gè)函數(shù)就可以了。
看了一下redux-thunk的源碼:
function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === "function") { return action(dispatch, getState, extraArgument); } return next(action); }; } const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware; export default thunk;
這段代碼跟上面我們看到的中間件沒有太大的差別,唯一一點(diǎn)就是對(duì)action做了一下如下判斷:
if (typeof action === "function") { return action(dispatch, getState, extraArgument); }
也就是說,如果發(fā)現(xiàn)actionCreater傳過來的action是一個(gè)函數(shù)的話,會(huì)執(zhí)行一下這個(gè)函數(shù),并以這個(gè)函數(shù)的返回值作為返回值。前面已經(jīng)說過,redux對(duì)dispatch函數(shù)的返回值不是很關(guān)心,因此此處也就無所謂了。
這樣的話,在我們的actionCreater中,我們就可以做任何的異步調(diào)用了,并且返回任何值也無所謂,所以我們可以使用promise了:
function actionCreate() { return function (dispatch, getState) { // 返回的函數(shù)體內(nèi)自由實(shí)現(xiàn)。。。 Ajax.fetch({xxx}).then(function (json) { dispatch(json); }) } }
最后還需要注意一點(diǎn),由于中間件只關(guān)心dispatch的傳遞,并不限制你做其他的事情,因此我們最好將redux-thunk放到中間件列表的首位,防止其他中間件中返回異步請(qǐng)求。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/81032.html
摘要:框架的使用詳解及教程在前段時(shí)間,我們也學(xué)習(xí)講解過框架的基本使用,但是有很多同學(xué)在交流群里給我的反饋信息說,框架理解上有難度,看了之后還是一臉懵逼不知道如何下手,很多同學(xué)就轉(zhuǎn)向選擇使用框架。 dva框架的使用詳解及Demo教程 在前段時(shí)間,我們也學(xué)習(xí)講解過Redux框架的基本使用,但是有很多同學(xué)在交流群里給我的反饋信息說,redux框架理解上有難度,看了之后還是一臉懵逼不知道如何下手,很...
摘要:利用中間件實(shí)現(xiàn)異步請(qǐng)求,實(shí)現(xiàn)兩個(gè)用戶角色實(shí)時(shí)通信。目前還未深入了解的一些概念。往后會(huì)寫更多的前后臺(tái)聯(lián)通的項(xiàng)目。刪除分組會(huì)連同組內(nèi)的所有圖片一起刪除。算是對(duì)自己上次用寫后臺(tái)的一個(gè)強(qiáng)化,項(xiàng)目文章在這里。后來一直沒動(dòng),前些日子才把后續(xù)的完善。 歡迎訪問我的個(gè)人網(wǎng)站:http://www.neroht.com/? 剛學(xué)vue和react時(shí),利用業(yè)余時(shí)間寫的關(guān)于這兩個(gè)框架的訓(xùn)練,都相對(duì)簡(jiǎn)單,有的...
摘要:并總結(jié)經(jīng)典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快速搭建項(xiàng)目。 本文是關(guān)注微信小程序的開發(fā)和面試問題,由基礎(chǔ)到困難循序漸進(jìn),適合面試和開發(fā)小程序。并總結(jié)vue React html css js 經(jīng)典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快...
摘要:并總結(jié)經(jīng)典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快速搭建項(xiàng)目。 本文是關(guān)注微信小程序的開發(fā)和面試問題,由基礎(chǔ)到困難循序漸進(jìn),適合面試和開發(fā)小程序。并總結(jié)vue React html css js 經(jīng)典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快...
閱讀 2478·2021-11-22 15:35
閱讀 3767·2021-11-04 16:14
閱讀 2696·2021-10-20 13:47
閱讀 2507·2021-10-13 09:49
閱讀 2078·2019-08-30 14:09
閱讀 2376·2019-08-26 13:49
閱讀 887·2019-08-26 10:45
閱讀 2778·2019-08-23 17:54