摘要:還有這個(gè)解決異步問題,極度不優(yōu)雅。是狀態(tài)管理的庫,是唯一控制頁面跳轉(zhuǎn)的庫。如果您計(jì)時(shí)旅行,它還會(huì)將新狀態(tài)傳遞給以再次更新組件樹。這使得從容器組件訪問它們變得容易。
Part01 What"s the problem
這段代碼意圖是把router傳遞props的路由信息再傳遞給redux。有這么幾個(gè)問題:
如果靠組件生命周期轉(zhuǎn)發(fā) 每個(gè)路由下面的頂級(jí)組件都要調(diào)這樣一個(gè)action
并且,如果路由有參數(shù)改變(很多時(shí)候頁面狀態(tài)的參數(shù)會(huì)在路由中體現(xiàn)),這段代碼是無法檢測(cè)的,還需要在componentWillReceiveProps里去處理邏輯。
還有這個(gè)setTimeout解決異步問題,極度不優(yōu)雅。
Can"t cooperateredux 是狀態(tài)管理的庫,router 是(唯一)控制頁面跳轉(zhuǎn)的庫。兩者都很美好,但是不美好的是兩者無法協(xié)同工作。換句話說,當(dāng)路由變化以后,store 無法感知到。
redux是想把絕大多數(shù)應(yīng)用程序的狀態(tài)都保存在單一的store里,而當(dāng)前的路由狀態(tài)明顯是應(yīng)用程序狀態(tài)很重要的一部分,應(yīng)當(dāng)是要保存在store中的。
目前是,如果直接使用react router,就意味著所有路由相關(guān)的信息脫離了Redux store的控制,假借組件接受router信息轉(zhuǎn)發(fā)dispatch的方法屬于反模式,違背了redux的設(shè)計(jì)思想,也給我們應(yīng)用程序帶來了更多的不確定性。
Part02 What do we need我們需要一個(gè)這樣的路由系統(tǒng),他技能利用React Router的聲明式特性,又能將路由信息整合進(jìn)Redux Store中。
react-router-reduxreact-router-redux 是 redux 的一個(gè)中間件(中間件:JavaScript 代理模式的另一種實(shí)踐 針對(duì) dispatch 實(shí)現(xiàn)了方法的代理,在 dispatch action 的時(shí)候增加或者修改) ,主要作用是:
加強(qiáng)了React Router庫中history這個(gè)實(shí)例,以允許將history中接受到的變化反應(yīng)到state中去。
import React from "react" import ReactDOM from "react-dom" import { createStore, combineReducers } from "redux" import { Provider } from "react-redux" import { Router, Route, browserHistory } from "react-router" import { syncHistoryWithStore, routerReducer } from "react-router-redux" import reducers from "/reducers" const store = createStore( combineReducers({ ...reducers, routing: routerReducer }) ) const history = syncHistoryWithStore(browserHistory, store) ReactDOM.render( , document.getElementById(‘a(chǎn)pp") )
使用簡(jiǎn)單直白的api syncHistoryWithStore來完成redux的綁定工作,我們只需要傳入react router中的history(前面提到的)以及redux中的store,就可以獲得一個(gè)增強(qiáng)后的history對(duì)象。
將這個(gè)history對(duì)象傳給react router中的Router組件作為props,就給應(yīng)用提供了觀察路由變化并改變store的能力。
現(xiàn)在,只要您按下瀏覽器按鈕或在應(yīng)用程序代碼中導(dǎo)航,導(dǎo)航就會(huì)首先通過Redux存儲(chǔ)區(qū)傳遞新位置,然后再傳遞到React Router以更新組件樹。如果您計(jì)時(shí)旅行,它還會(huì)將新狀態(tài)傳遞給React Router以再次更新組件樹。
React Router?通過路徑組件的props提供路由信息。這使得從容器組件訪問它們變得容易。當(dāng)使用react-redux對(duì)connect()你的組件進(jìn)行陳述時(shí),你可以從第二個(gè)參數(shù)mapStateToProps訪問路由器的道具:
Part04 Code principle https://github.com/reactjs/react-router-redux// index.js /** * 作為外部 syncHistoryWithStore * 綁定store.dispatch方法引起的state中路由狀態(tài)變更到影響瀏覽器location變更 * 綁定瀏覽器location變更觸發(fā)store.dispatch,更新state中路由狀態(tài) * 返回當(dāng)前的histroy、綁定方法listen(dispatch方法觸發(fā)時(shí)執(zhí)行,以綁定前的路由狀態(tài)為參數(shù))、解綁函數(shù)unsubscribe */ export syncHistoryWithStore from "./sync" /** * routerReducer監(jiān)聽路由變更子reducer,通過redux的combineReducers復(fù)合多個(gè)reducer后使用 */ export { LOCATION_CHANGE, routerReducer } from "./reducer" /** * 構(gòu)建actionCreater,作為外部push、replace、go、goBack、goForward方法的接口,通常不直接使用 */ export { CALL_HISTORY_METHOD, push, replace, go, goBack, goForward, routerActions } from "./actions" /** * 構(gòu)建route中間件,用于分發(fā)action,觸發(fā)路徑跳轉(zhuǎn)等事件 */ export routerMiddleware from "./middleware"
// sync.js import { LOCATION_CHANGE } from "./reducer" // 默認(rèn)用state.routing存取route變更狀態(tài)數(shù)據(jù) const defaultSelectLocationState = state => state.routing /** * 作為外部syncHistoryWithStore接口方法 * 綁定store.dispatch方法引起的state中路由狀態(tài)變更到影響瀏覽器location變更 * 綁定瀏覽器location變更觸發(fā)store.dispatch,更新state中路由狀態(tài) * 返回當(dāng)前的histroy、綁定方法listen(dispatch方法觸發(fā)時(shí)執(zhí)行,以綁定前的路由狀態(tài)為參數(shù))、解綁函數(shù)unsubscribe */ export default function syncHistoryWithStore(history, store, { // 約定redux.store.state中哪個(gè)屬性用于存取route變更狀態(tài)數(shù)據(jù) selectLocationState = defaultSelectLocationState, // store中路由狀態(tài)變更是否引起瀏覽器location改變 adjustUrlOnReplay = true } = {}) { // Ensure that the reducer is mounted on the store and functioning properly. // 確保redux.store.state中某個(gè)屬性綁定了route變更狀態(tài) if (typeof selectLocationState(store.getState()) === "undefined") { throw new Error( "Expected the routing state to be available either as `state.routing` " + "or as the custom expression you can specify as `selectLocationState` " + "in the `syncHistoryWithStore()` options. " + "Ensure you have added the `routerReducer` to your store"s " + "reducers via `combineReducers` or whatever method you use to isolate " + "your reducers." ) } let initialLocation // 初始化route狀態(tài)數(shù)據(jù) let isTimeTraveling // 瀏覽器頁面location.url改變過程中標(biāo)識(shí),區(qū)別頁面鏈接及react-router-redux變更location兩種情況 let unsubscribeFromStore // 移除store.listeners中,因路由狀態(tài)引起瀏覽器location變更函數(shù) let unsubscribeFromHistory // 移除location變更引起路由狀態(tài)更新函數(shù) let currentLocation // 記錄上一個(gè)當(dāng)前路由狀態(tài)數(shù)據(jù) // 獲取路由事件觸發(fā)后路由狀態(tài),或者useInitialIfEmpty為真值獲取初始化route狀態(tài),或者undefined const getLocationInStore = (useInitialIfEmpty) => { const locationState = selectLocationState(store.getState()) return locationState.locationBeforeTransitions || (useInitialIfEmpty ? initialLocation : undefined) } // 初始化route狀態(tài)數(shù)據(jù) initialLocation = getLocationInStore() // If the store is replayed, update the URL in the browser to match. // adjustUrlOnReplay為真值時(shí),store數(shù)據(jù)改變事件dispatch發(fā)生后,瀏覽器頁面更新location if (adjustUrlOnReplay) { // 由store中路由狀態(tài)改變情況,更新瀏覽器location const handleStoreChange = () => { // 獲取路由事件觸發(fā)后路由狀態(tài),或者初始路由狀態(tài) const locationInStore = getLocationInStore(true) if (currentLocation === locationInStore || initialLocation === locationInStore) { return } // 瀏覽器頁面location.url改變過程中標(biāo)識(shí),區(qū)別頁面鏈接及react-router-redux變更location兩種情況 isTimeTraveling = true // 記錄上一個(gè)當(dāng)前路由狀態(tài)數(shù)據(jù) currentLocation = locationInStore // store數(shù)據(jù)改變后,瀏覽器頁面更新location history.transitionTo({ ...locationInStore, action: "PUSH" }) isTimeTraveling = false } // 綁定事件,完成功能為,dispatch方法觸發(fā)store中路由狀態(tài)改變時(shí),更新瀏覽器location unsubscribeFromStore = store.subscribe(handleStoreChange) // 初始化設(shè)置路由狀態(tài)時(shí)引起頁面location改變 handleStoreChange() } // 頁面鏈接變更瀏覽器location,觸發(fā)store.dispatch變更store中路由狀態(tài) const handleLocationChange = (location) => { // react-router-redux引起瀏覽器location變更過程中,無效;頁面鏈接變更,有效 if (isTimeTraveling) { return } // Remember where we are currentLocation = location // Are we being called for the first time? if (!initialLocation) { // Remember as a fallback in case state is reset initialLocation = location // Respect persisted location, if any if (getLocationInStore()) { return } } // Tell the store to update by dispatching an action store.dispatch({ type: LOCATION_CHANGE, payload: location }) } // hashHistory、boswerHistory監(jiān)聽瀏覽器location變更,觸發(fā)store.dispatch變更store中路由狀態(tài) unsubscribeFromHistory = history.listen(handleLocationChange) // History 3.x doesn"t call listen synchronously, so fire the initial location change ourselves // 初始化更新store中路由狀態(tài) if (history.getCurrentLocation) { handleLocationChange(history.getCurrentLocation()) } // The enhanced history uses store as source of truth return { ...history, // store中dispatch方法觸發(fā)時(shí),綁定執(zhí)行函數(shù)listener,以綁定前的路由狀態(tài)為參數(shù) listen(listener) { // Copy of last location. // 綁定前的路由狀態(tài) let lastPublishedLocation = getLocationInStore(true) // Keep track of whether we unsubscribed, as Redux store // only applies changes in subscriptions on next dispatch let unsubscribed = false // 確保listener在解綁后不執(zhí)行 const unsubscribeFromStore = store.subscribe(() => { const currentLocation = getLocationInStore(true) if (currentLocation === lastPublishedLocation) { return } lastPublishedLocation = currentLocation if (!unsubscribed) { listener(lastPublishedLocation) } }) // History 2.x listeners expect a synchronous call. Make the first call to the // listener after subscribing to the store, in case the listener causes a // location change (e.g. when it redirects) if (!history.getCurrentLocation) { listener(lastPublishedLocation) } // Let user unsubscribe later return () => { unsubscribed = true unsubscribeFromStore() } }, // 解綁函數(shù),包括location到store的handleLocationChange、store到location的handleStoreChange unsubscribe() { if (adjustUrlOnReplay) { unsubscribeFromStore() } unsubscribeFromHistory() } } }
// reducer.js /** * This action type will be dispatched when your history * receives a location change. */ export const LOCATION_CHANGE = "@@router/LOCATION_CHANGE" const initialState = { locationBeforeTransitions: null } /** * 監(jiān)聽路由變更子reducer,通過redux的combineReducers復(fù)合多個(gè)reducer后使用,作為外部routerReducer接口 * 提示redux使用過程中,可通過子組件模塊中注入reducer,再使用combineReducers復(fù)合多個(gè)reducer * 最后使用replaceReducer方法更新當(dāng)前store的reducer,意義是構(gòu)建reducer拆解到各個(gè)子模塊中 * */ export function routerReducer(state = initialState, { type, payload } = {}) { if (type === LOCATION_CHANGE) { return { ...state, locationBeforeTransitions: payload } } return state }
// actions.js export const CALL_HISTORY_METHOD = "@@router/CALL_HISTORY_METHOD" function updateLocation(method) { return (...args) => ({ type: CALL_HISTORY_METHOD, // route事件標(biāo)識(shí),避免和用于定義的action沖突 payload: { method, args } // method系hashHistroy、boswerHistroy對(duì)外接口方法名,args為參數(shù) }) } /** * 返回actionCreater,作為外部push、replace、go、goBack、goForward方法的接口,通常不直接使用 */ export const push = updateLocation("push") export const replace = updateLocation("replace") export const go = updateLocation("go") export const goBack = updateLocation("goBack") export const goForward = updateLocation("goForward") export const routerActions = { push, replace, go, goBack, goForward }完結(jié)
(此文由PPT摘抄完成)PPT鏈接
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/99797.html
摘要:服務(wù)端渲染的手腳架這個(gè)啟動(dòng)包的設(shè)計(jì)是為了讓你使用一整套最新最酷的前端技術(shù),所有都是可配置,富特性,基于已經(jīng)提供代碼熱加載,使用預(yù)處理,單元測(cè)試,代碼覆蓋率報(bào)告,代碼分割等等更多。 Universal React Starter Kit 服務(wù)端渲染的React手腳架 這個(gè)啟動(dòng)包的設(shè)計(jì)是為了讓你使用一整套最新最酷的前端技術(shù),所有都是可配置,富特性,基于webpack已經(jīng)提供代碼熱加載,使用...
摘要:服務(wù)端渲染的手腳架這個(gè)啟動(dòng)包的設(shè)計(jì)是為了讓你使用一整套最新最酷的前端技術(shù),所有都是可配置,富特性,基于已經(jīng)提供代碼熱加載,使用預(yù)處理,單元測(cè)試,代碼覆蓋率報(bào)告,代碼分割等等更多。 Universal React Starter Kit 服務(wù)端渲染的React手腳架 這個(gè)啟動(dòng)包的設(shè)計(jì)是為了讓你使用一整套最新最酷的前端技術(shù),所有都是可配置,富特性,基于webpack已經(jīng)提供代碼熱加載,使用...
摘要:服務(wù)端渲染的手腳架這個(gè)啟動(dòng)包的設(shè)計(jì)是為了讓你使用一整套最新最酷的前端技術(shù),所有都是可配置,富特性,基于已經(jīng)提供代碼熱加載,使用預(yù)處理,單元測(cè)試,代碼覆蓋率報(bào)告,代碼分割等等更多。 Universal React Starter Kit 服務(wù)端渲染的React手腳架 這個(gè)啟動(dòng)包的設(shè)計(jì)是為了讓你使用一整套最新最酷的前端技術(shù),所有都是可配置,富特性,基于webpack已經(jīng)提供代碼熱加載,使用...
摘要:下面會(huì)從淺到深,淡淡在閱讀源碼過程中自己的理解。分拆子頁面后,每一個(gè)子頁面對(duì)應(yīng)一個(gè)文件。總結(jié)上面就是最早版本的源碼,很簡(jiǎn)潔的使用了等其目的也很簡(jiǎn)單簡(jiǎn)化相關(guān)生態(tài)的繁瑣邏輯參考源碼地址 ??dva的思想還是很不錯(cuò)的,大大提升了開發(fā)效率,dva集成了Redux以及Redux的中間件Redux-saga,以及React-router等等。得益于Redux的狀態(tài)管理,以及Redux-saga中...
閱讀 1660·2021-11-23 10:07
閱讀 2653·2019-08-30 11:10
閱讀 2834·2019-08-29 17:08
閱讀 1778·2019-08-29 15:42
閱讀 3163·2019-08-29 12:57
閱讀 2396·2019-08-28 18:06
閱讀 3544·2019-08-27 10:56
閱讀 383·2019-08-26 11:33