摘要:編輯器頂層組件不就了嗎這就是。官方提供的綁定庫。具有高效且靈活的特性。在的中,可以使用或者等來監聽某個,當某個觸發后,可以使用發起異步操作,操作完成后使用函數觸發,同步更新,從而完成整個的更新。
不就ok了嗎?這就是 react-redux。Redux 官方提供的 React 綁定庫。 具有高效且靈活的特性。
React Redux 將組件區分為 容器組件 和 UI 組件
前者會處理邏輯
后者只負責顯示和交互,內部不處理邏輯,狀態完全由外部掌控
兩個核心
Provider
看我上邊那個代碼的頂層組件4個字。對,你沒有猜錯。這個頂級組件就是Provider,一般我們都將頂層組件包裹在Provider組件之中,這樣的話,所有組件就都可以在react-redux的控制之下了,但是store必須作為參數放到Provider組件中去
這個組件的目的是讓所有組件都能夠訪問到Redux中的數據。
connect
這個才是react-redux中比較難的部分,我們詳細解釋一下
首先,先記住下邊的這行代碼:
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
這個單詞翻譯過來就是把state映射到props中去 ,其實也就是把Redux中的數據映射到React中的props中去。
舉個栗子:
const mapStateToProps = (state) => { return { // prop : state.xxx | 意思是將state中的某個數據映射到props中 foo: state.bar } }
然后渲染的時候就可以使用this.props.foo
class Foo extends Component { constructor(props){ super(props); } render(){ return( // 這樣子渲染的其實就是state.bar的數據了
this.props.foo) } } Foo = connect()(Foo); export default Foo;
然后這樣就可以完成渲染了
這個單詞翻譯過來就是就是把各種dispatch也變成了props讓你可以直接使用
const mapDispatchToProps = (dispatch) => { // 默認傳遞參數就是dispatch return { onClick: () => { dispatch({ type: "increatment" }); } }; }
class Foo extends Component { constructor(props){ super(props); } render(){ return( ) } } Foo = connect()(Foo); export default Foo;
組件也就改成了上邊這樣,可以直接通過this.props.onClick,來調用dispatch,這樣子就不需要在代碼中來進行store.dispatch了
react-redux的基本介紹就到這里了
redux-saga
如果按照原始的redux工作流程,當組件中產生一個action后會直接觸發reducer修改state,reducer又是一個純函數,也就是不能再reducer中進行異步操作;
而往往實際中,組件中發生的action后,在進入reducer之前需要完成一個異步任務,比如發送ajax請求后拿到數據后,再進入reducer,顯然原生的redux是不支持這種操作的
這個時候急需一個中間件來處理這種業務場景,目前最優雅的處理方式自然就是redux-saga
核心講解
1、Saga 輔助函數
redux-saga提供了一些輔助函數,用來在一些特定的action 被發起到Store時派生任務,下面我先來講解兩個輔助函數:takeEvery 和 takeLatest
takeEvery就像一個流水線的洗碗工,過來一個臟盤子就直接執行后面的洗碗函數,一旦你請了這個洗碗工他會一直執行這個工作,絕對不會停止接盤子的監聽過程和觸發洗盤子函數
例如:每次點擊 按鈕去Fetch獲取數據時時,我們發起一個 FETCH_REQUESTED 的 action。 我們想通過啟動一個任務從服務器獲取一些數據,來處理這個action,類似于
window.addEventLister("xxx",fn)
當dispatch xxx的時候,就會執行fn方法,
首先我們創建一個將執行異步 action 的任務(也就是上邊的fn):
// put:你就認為put就等于 dispatch就可以了; // call:可以理解為實行一個異步函數,是阻塞型的,只有運行完后面的函數,才會繼續往下; // 在這里可以片面的理解為async中的await!但寫法直觀多了! import { call, put } from "redux-saga/effects" export function* fetchData(action) { try { const apiAjax = (params) => fetch(url, params); const data = yield call(apiAjax); yield put({type: "FETCH_SUCCEEDED", data}); } catch (error) { yield put({type: "FETCH_FAILED", error}); } }
然后在每次 FETCH_REQUESTED action 被發起時啟動上面的任務,也就相當于每次觸發一個名字為 FETCH_REQUESTED 的action就會執行上邊的任務,代碼如下
import { takeEvery } from "redux-saga" function* watchFetchData() { yield* takeEvery("FETCH_REQUESTED", fetchData) }
注意:上面的 takeEvery 函數可以使用下面的寫法替換
function* watchFetchData() { while(true){ yield take("FETCH_REQUESTED"); yield fork(fetchData); } }
在上面的例子中,takeEvery 允許多個 fetchData 實例同時啟動,在某個特定時刻,我們可以啟動一個新的 fetchData 任務, 盡管之前還有一個或多個 fetchData 尚未結束
如果我們只想得到最新那個請求的響應(例如,始終顯示最新版本的數據),我們可以使用 takeLatest 輔助函數
import { takeLatest } from "redux-saga" function* watchFetchData() { yield* takeLatest("FETCH_REQUESTED", fetchData) }
和takeEvery不同,在任何時刻 takeLatest 只允許執行一個 fetchData 任務,并且這個任務是最后被啟動的那個,如果之前已經有一個任務在執行,那之前的這個任務會自動被取消
2、Effect Creators
redux-saga框架提供了很多創建effect的函數,下面我們就來簡單的介紹下開發中最常用的幾種
take(pattern)
put(action)
call(fn, ...args)
fork(fn, ...args)
select(selector, ...args)
take函數可以理解為監聽未來的action,它創建了一個命令對象,告訴middleware等待一個特定的action, Generator會暫停,直到一個與pattern匹配的action被發起,才會繼續執行下面的語句,也就是說,take是一個阻塞的 effect
用法:
function* watchFetchData() { while(true) { // 監聽一個type為 "FETCH_REQUESTED" 的action的執行,直到等到這個Action被觸發,才會接著執行下面的 yield fork(fetchData) 語句 yield take("FETCH_REQUESTED"); yield fork(fetchData); } }
put函數是用來發送action的 effect,你可以簡單的把它理解成為redux框架中的dispatch函數,當put一個action后,reducer中就會計算新的state并返回,注意: put 也是阻塞 effect
用法:
export function* toggleItemFlow() { let list = [] // 發送一個type為 "UPDATE_DATA" 的Action,用來更新數據,參數為 `data:list` yield put({ type: actionTypes.UPDATE_DATA, data: list }) }
call函數你可以把它簡單的理解為就是可以調用其他函數的函數,它命令 middleware 來調用fn 函數, args為函數的參數,注意: fn 函數可以是一個 Generator 函數,也可以是一個返回 Promise 的普通函數,call 函數也是阻塞 effect
用法:
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) export function* removeItem() { try { // 這里call 函數就調用了 delay 函數,delay 函數為一個返回promise 的函數 return yield call(delay, 500) } catch (err) { yield put({type: actionTypes.ERROR}) } }
fork 函數和 call 函數很像,都是用來調用其他函數的,但是fork函數是非阻塞函數,也就是說,程序執行完 yield fork(fn, args) 這一行代碼后,會立即接著執行下一行代碼語句,而不會等待fn函數返回結果后,在執行下面的語句
用法:
import { fork } from "redux-saga/effects" export default function* rootSaga() { // 下面的四個 Generator 函數會一次執行,不會阻塞執行 yield fork(addItemFlow) yield fork(removeItemFlow) yield fork(toggleItemFlow) yield fork(modifyItem) }
select 函數是用來指示 middleware調用提供的選擇器獲取Store上的state數據,你也可以簡單的把它理解為redux框架中獲取store上的 state數據一樣的功能 :store.getState()
用法:
export function* toggleItemFlow() { // 通過 select effect 來獲取 全局 state上的 `getTodoList` 中的 list let tempList = yield select(state => state.getTodoList.list) }
一個具體的實例
**index.js **
import React from "react"; import ReactDOM from "react-dom"; import {createStore, applyMiddleware} from "redux" import createSagaMiddleware from "redux-saga" import rootSaga from "./sagas" import Counter from "./Counter" import rootReducer from "./reducers" const sagaMiddleware = createSagaMiddleware() // 創建了一個saga中間件實例 // 下邊這句話和下邊的兩行代碼創建store的方式是一樣的 // const store = createStore(reducers,applyMiddlecare(middlewares)) const createStoreWithMiddleware = applyMiddleware(middlewares)(createStore) const store = createStoreWithMiddleware(rootReducer) sagaMiddleware.run(rootSaga) const action = type => store.dispatch({ type }) function render() { ReactDOM.render(
action("INCREMENT")} onDecrement={() => action("DECREMENT")} onIncrementAsync={() => action("INCREMENT_ASYNC")} />, document.getElementById("root") ) } render() store.subscribe(render)
sagas.js
import { put, call, take,fork } from "redux-saga/effects"; import { takeEvery, takeLatest } from "redux-saga" export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); function* incrementAsync() { // 延遲 1s 在執行 + 1操作 yield call(delay, 1000); yield put({ type: "INCREMENT" }); } export default function* rootSaga() { // while(true){ // yield take("INCREMENT_ASYNC"); // yield fork(incrementAsync); // } // 下面的寫法與上面的寫法上等效 yield* takeEvery("INCREMENT_ASYNC", incrementAsync) }
reducer.js
export default function counter(state = 0, action) { switch (action.type) { case "INCREMENT": return state + 1 case "DECREMENT": return state - 1 case "INCREMENT_ASYNC": return state default: return state } }
從上面的代碼結構可以看出,redux-saga的使用方式還是比較簡單的,相比較之前的redux框架的CounterApp,多了一個sagas的文件,reducers文件還是之前的使用方式
redux-saga基本用法總結:
使用 createSagaMiddleware 方法創建 saga 的 Middleware ,然后在創建的 redux 的 store 時,使用 applyMiddleware 函數將創建的 saga Middleware 實例綁定到 store 上,最后可以調用 saga Middleware 的 run 函數來執行某個或者某些 Middleware 。
在 saga 的 Middleware 中,可以使用 takeEvery 或者 takeLatest 等 API 來監聽某個 action ,當某個 action 觸發后, saga 可以使用 call 發起異步操作,操作完成后使用 put 函數觸發 action ,同步更新 state ,從而完成整個 State 的更新。
ok,故事到這里就接近尾聲了,以上主要介紹了redux,react-redux和redux-saga目前redux全家桶主流的一些產品,接下來,主要會產出一下根據源碼,手寫一下redux和react-redux的輪子
希望各位大佬點贊,關注,鼓勵一下,不足之處,還望斧正~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/6659.html
摘要:項目的架構也是最近在各種探討研究。還求大神多指點項目技術總結技術棧項目結構探究初體驗關于項目中的配置說明項目簡單說明開發這一套,我個人的理解是體現的是代碼分層職責分離的編程思想邏輯與視圖嚴格區分。前端依舊使用技術棧完成。 項目地址:https://github.com/Nealyang/R...技術棧:react、react-router4.x 、 react-redux 、 webp...
摘要:背景項目用的是全家桶,之前有同事用改進了一波,一直都沒去研究。這次的打算寫一個輸入框,輸入拼音會返回對應的城市列表。 背景 項目用的是react全家桶, 之前有同事用redux-saga 改進了一波, 一直都沒去研究。 前幾天趁有空,也去學習了下, 寫了個簡單的demo練練手, 在這里簡單分享一下。 這次的demo打算寫一個輸入框,輸入拼音會返回對應的城市列表。并盡可能多的使用redu...
摘要:更多參考通過庫掌握函數組件有些時候,我們想要動態的加載一些組件按需加載,比如在一個單頁面應用中頁面的組件頁面的組件頁面的組件只有真正要實例化當前頁面的時候,才會去加載相應的組件。 react 前端項目技術選型、開發工具、周邊生態 聲明:這不是一篇介紹 React 基礎知識的文章,需要熟悉 React 相關知識 主架構:react, react-router, redux, redux...
摘要:更多參考通過庫掌握函數組件有些時候,我們想要動態的加載一些組件按需加載,比如在一個單頁面應用中頁面的組件頁面的組件頁面的組件只有真正要實例化當前頁面的時候,才會去加載相應的組件。 react 前端項目技術選型、開發工具、周邊生態 聲明:這不是一篇介紹 React 基礎知識的文章,需要熟悉 React 相關知識 主架構:react, react-router, redux, redux...
閱讀 2514·2023-04-25 17:37
閱讀 1195·2021-11-24 10:29
閱讀 3701·2021-09-09 11:57
閱讀 698·2021-08-10 09:41
閱讀 2249·2019-08-30 15:55
閱讀 2817·2019-08-30 15:54
閱讀 1949·2019-08-30 15:53
閱讀 901·2019-08-30 15:43