小編給大家分享一下redux、react-redux、redux-saga的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
“專業(yè)、務實、高效、創(chuàng)新、把客戶的事當成自己的事”是我們每一個人一直以來堅持追求的企業(yè)文化。 創(chuàng)新互聯(lián)是您可以信賴的網站建設服務商、專業(yè)的互聯(lián)網服務提供商! 專注于成都網站設計、網站建設、軟件開發(fā)、設計服務業(yè)務。我們始終堅持以客戶需求為導向,結合用戶體驗與視覺傳達,提供有針對性的項目解決方案,提供專業(yè)性的建議,創(chuàng)新互聯(lián)建站將不斷地超越自我,追逐市場,引領市場!redux前言
首先,本篇文章要求您對js,react等知識有一定的了解,如果不曾了解,建議您先看一下:React精髓!一篇全概括(急速)
React有props和state:
props意味著父級分發(fā)下來的屬性
state意味著組件內部可以自行管理的狀態(tài),并且整個React沒有數(shù)據向上回溯的能力,這就是react的單向數(shù)據流
這就意味著如果是一個數(shù)據狀態(tài)非常復雜的應用,更多的時候發(fā)現(xiàn)React根本無法讓兩個組件互相交流,使用對方的數(shù)據,react的通過層級傳遞數(shù)據的這種方法是非常難受的,這個時候,迫切需要一個機制,把所有的state集中到組件頂部,能夠靈活的將所有state各取所需的分發(fā)給所有的組件,是的,這就是redux
簡介
redux是的誕生是為了給 React 應用提供「可預測化的狀態(tài)管理」機制。
Redux會將整個應用狀態(tài)(其實也就是數(shù)據)存儲到到一個地方,稱為store
這個store里面保存一棵狀態(tài)樹(state tree)
組件改變state的唯一方法是通過調用store的dispatch方法,觸發(fā)一個action,這個action被對應的reducer處理,于是state完成更新
組件可以派發(fā)(dispatch)行為(action)給store,而不是直接通知其它組件
其它組件可以通過訂閱store中的狀態(tài)(state)來刷新自己的視圖
使用步驟
創(chuàng)建reducer
可以使用單獨的一個reducer,也可以將多個reducer合并為一個reducer,即:combineReducers()
action發(fā)出命令后將state放入reucer加工函數(shù)中,返回新的state,對state進行加工處理
創(chuàng)建action
用戶是接觸不到state的,只能有view觸發(fā),所以,這個action可以理解為指令,需要發(fā)出多少動作就有多少指令
action是一個對象,必須有一個叫type的參數(shù),定義action類型
創(chuàng)建的store,使用createStore方法
store 可以理解為有多個加工機器的總工廠
提供subscribe,dispatch,getState這些方法。
按步驟手把手實戰(zhàn)。
上述步驟,對應的序號,我會在相關代碼標出
npm install redux -S // 安裝 import { createStore } from 'redux' // 引入 const reducer = (state = {count: 0}, action) => {----------> ⑴ switch (action.type){ case 'INCREASE': return {count: state.count + 1}; case 'DECREASE': return {count: state.count - 1}; default: return state; } } const actions = {---------->⑵ increase: () => ({type: 'INCREASE'}), decrease: () => ({type: 'DECREASE'}) } const store = createStore(reducer);---------->⑶ store.subscribe(() => console.log(store.getState()) ); store.dispatch(actions.increase()) // {count: 1} store.dispatch(actions.increase()) // {count: 2} store.dispatch(actions.increase()) // {count: 3}
自己畫了一張非常簡陋的流程圖,方便理解redux的工作流程
react-redux剛開始就說了,如果把store直接集成到React應用的頂層props里面,只要各個子組件能訪問到頂層props就行了,比如這樣:
<頂層組件 store={store}> <App /> </頂層組件>
不就ok了嗎?這就是 react-redux。Redux 官方提供的 React 綁定庫。 具有高效且靈活的特性。
React Redux 將組件區(qū)分為 容器組件 和 UI 組件
前者會處理邏輯
后者只負責顯示和交互,內部不處理邏輯,狀態(tài)完全由外部掌控
兩個核心
Provider
看我上邊那個代碼的頂層組件4個字。對,你沒有猜錯。這個頂級組件就是Provider,一般我們都將頂層組件包裹在Provider組件之中,這樣的話,所有組件就都可以在react-redux的控制之下了,但是store必須作為參數(shù)放到Provider組件中去
<Provider store = {store}> <App /> <Provider>
這個組件的目的是讓所有組件都能夠訪問到Redux中的數(shù)據。
connect
這個才是react-redux中比較難的部分,我們詳細解釋一下
首先,先記住下邊的這行代碼:
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
mapStateToProps
這個單詞翻譯過來就是把state映射到props中去 ,其實也就是把Redux中的數(shù)據映射到React中的props中去。
舉個栗子:
const mapStateToProps = (state) => { return { // prop : state.xxx | 意思是將state中的某個數(shù)據映射到props中 foo: state.bar } }
然后渲染的時候就可以使用this.props.foo
class Foo extends Component { constructor(props){ super(props); } render(){ return( // 這樣子渲染的其實就是state.bar的數(shù)據了 <div>this.props.foo</div> ) } } Foo = connect()(Foo); export default Foo;
然后這樣就可以完成渲染了
mapDispatchToProps
這個單詞翻譯過來就是就是把各種dispatch也變成了props讓你可以直接使用
const mapDispatchToProps = (dispatch) => { // 默認傳遞參數(shù)就是dispatch return { onClick: () => { dispatch({ type: 'increatment' }); } }; }
class Foo extends Component { constructor(props){ super(props); } render(){ return( <button onClick = {this.props.onClick}>點擊increase</button> ) } } Foo = connect()(Foo); export default Foo;
組件也就改成了上邊這樣,可以直接通過this.props.onClick,來調用dispatch,這樣子就不需要在代碼中來進行store.dispatch了
react-redux的基本介紹就到這里了
redux-saga如果按照原始的redux工作流程,當組件中產生一個action后會直接觸發(fā)reducer修改state,reducer又是一個純函數(shù),也就是不能再reducer中進行異步操作;
而往往實際中,組件中發(fā)生的action后,在進入reducer之前需要完成一個異步任務,比如發(fā)送ajax請求后拿到數(shù)據后,再進入reducer,顯然原生的redux是不支持這種操作的
這個時候急需一個中間件來處理這種業(yè)務場景,目前最優(yōu)雅的處理方式自然就是redux-saga
核心講解
1、Saga 輔助函數(shù)
redux-saga提供了一些輔助函數(shù),用來在一些特定的action 被發(fā)起到Store時派生任務,下面我先來講解兩個輔助函數(shù):takeEvery 和 takeLatest
takeEvery
takeEvery就像一個流水線的洗碗工,過來一個臟盤子就直接執(zhí)行后面的洗碗函數(shù),一旦你請了這個洗碗工他會一直執(zhí)行這個工作,絕對不會停止接盤子的監(jiān)聽過程和觸發(fā)洗盤子函數(shù)
例如:每次點擊 按鈕去Fetch獲取數(shù)據時時,我們發(fā)起一個 FETCH_REQUESTED 的 action。 我們想通過啟動一個任務從服務器獲取一些數(shù)據,來處理這個action,類似于
window.addEventLister('xxx',fn)
當dispatch xxx的時候,就會執(zhí)行fn方法,
首先我們創(chuàng)建一個將執(zhí)行異步 action 的任務(也就是上邊的fn):
// put:你就認為put就等于 dispatch就可以了; // call:可以理解為實行一個異步函數(shù),是阻塞型的,只有運行完后面的函數(shù),才會繼續(xù)往下; // 在這里可以片面的理解為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 被發(fā)起時啟動上面的任務,也就相當于每次觸發(fā)一個名字為 FETCH_REQUESTED 的action就會執(zhí)行上邊的任務,代碼如下
import { takeEvery } from 'redux-saga' function* watchFetchData() { yield* takeEvery("FETCH_REQUESTED", fetchData) }
注意:上面的 takeEvery 函數(shù)可以使用下面的寫法替換
function* watchFetchData() { while(true){ yield take('FETCH_REQUESTED'); yield fork(fetchData); } }
takeLatest
在上面的例子中,takeEvery 允許多個 fetchData 實例同時啟動,在某個特定時刻,我們可以啟動一個新的 fetchData 任務, 盡管之前還有一個或多個 fetchData 尚未結束
如果我們只想得到最新那個請求的響應(例如,始終顯示最新版本的數(shù)據),我們可以使用 takeLatest 輔助函數(shù)
import { takeLatest } from 'redux-saga' function* watchFetchData() { yield* takeLatest('FETCH_REQUESTED', fetchData) }
和takeEvery不同,在任何時刻 takeLatest 只允許執(zhí)行一個 fetchData 任務,并且這個任務是最后被啟動的那個,如果之前已經有一個任務在執(zhí)行,那之前的這個任務會自動被取消
2、Effect Creators
redux-saga框架提供了很多創(chuàng)建effect的函數(shù),下面我們就來簡單的介紹下開發(fā)中最常用的幾種
take(pattern)
put(action)
call(fn, ...args)
fork(fn, ...args)
select(selector, ...args)
take(pattern)
take函數(shù)可以理解為監(jiān)聽未來的action,它創(chuàng)建了一個命令對象,告訴middleware等待一個特定的action, Generator會暫停,直到一個與pattern匹配的action被發(fā)起,才會繼續(xù)執(zhí)行下面的語句,也就是說,take是一個阻塞的 effect
用法:
function* watchFetchData() { while(true) { // 監(jiān)聽一個type為 'FETCH_REQUESTED' 的action的執(zhí)行,直到等到這個Action被觸發(fā),才會接著執(zhí)行下面的 yield fork(fetchData) 語句 yield take('FETCH_REQUESTED'); yield fork(fetchData); } }
put(action)
put函數(shù)是用來發(fā)送action的 effect,你可以簡單的把它理解成為redux框架中的dispatch函數(shù),當put一個action后,reducer中就會計算新的state并返回,注意: put 也是阻塞 effect
用法:
export function* toggleItemFlow() { let list = [] // 發(fā)送一個type為 'UPDATE_DATA' 的Action,用來更新數(shù)據,參數(shù)為 `data:list` yield put({ type: actionTypes.UPDATE_DATA, data: list }) }
call(fn, ...args)
call函數(shù)你可以把它簡單的理解為就是可以調用其他函數(shù)的函數(shù),它命令 middleware 來調用fn 函數(shù), args為函數(shù)的參數(shù),注意: fn 函數(shù)可以是一個 Generator 函數(shù),也可以是一個返回 Promise 的普通函數(shù),call 函數(shù)也是阻塞 effect
用法:
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) export function* removeItem() { try { // 這里call 函數(shù)就調用了 delay 函數(shù),delay 函數(shù)為一個返回promise 的函數(shù) return yield call(delay, 500) } catch (err) { yield put({type: actionTypes.ERROR}) } }
fork(fn, ...args)
fork 函數(shù)和 call 函數(shù)很像,都是用來調用其他函數(shù)的,但是fork函數(shù)是非阻塞函數(shù),也就是說,程序執(zhí)行完 yield fork(fn, args) 這一行代碼后,會立即接著執(zhí)行下一行代碼語句,而不會等待fn函數(shù)返回結果后,在執(zhí)行下面的語句
用法:
import { fork } from 'redux-saga/effects' export default function* rootSaga() { // 下面的四個 Generator 函數(shù)會一次執(zhí)行,不會阻塞執(zhí)行 yield fork(addItemFlow) yield fork(removeItemFlow) yield fork(toggleItemFlow) yield fork(modifyItem) }
select(selector, ...args)
select 函數(shù)是用來指示 middleware調用提供的選擇器獲取Store上的state數(shù)據,你也可以簡單的把它理解為redux框架中獲取store上的 state數(shù)據一樣的功能 :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() // 創(chuàng)建了一個saga中間件實例 // 下邊這句話和下邊的兩行代碼創(chuàng)建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( <Counter value={store.getState()} onIncrement={() => 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 在執(zhí)行 + 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 方法創(chuàng)建 saga 的 Middleware ,然后在創(chuàng)建的 redux 的 store 時,使用 applyMiddleware 函數(shù)將創(chuàng)建的 saga Middleware 實例綁定到 store 上,最后可以調用 saga Middleware 的 run 函數(shù)來執(zhí)行某個或者某些 Middleware 。
在 saga 的 Middleware 中,可以使用 takeEvery 或者 takeLatest 等 API 來監(jiān)聽某個 action ,當某個 action 觸發(fā)后, saga 可以使用 call 發(fā)起異步操作,操作完成后使用 put 函數(shù)觸發(fā) action ,同步更新 state ,從而完成整個 State 的更新。
以上是“redux、react-redux、redux-saga的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
本文標題:redux、react-redux、redux-saga的示例分析-創(chuàng)新互聯(lián)
路徑分享:http://bm7419.com/article26/ighjg.html
成都網站建設公司_創(chuàng)新互聯(lián),為您提供App開發(fā)、品牌網站設計、手機網站建設、微信小程序、虛擬主機、網站設計公司
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內容