Redux学习(2) ----- 异步和中间件
Redux中間件,其實就是一個函數(shù), 當(dāng)我們發(fā)送一個action的時候,先經(jīng)過它,我們就可以對action進(jìn)行處理,然后再發(fā)送action到達(dá)reducer, 改變狀態(tài),這時我們就可以在中間件中,對action 和對應(yīng)的state進(jìn)行跟蹤,有利于bug處理,還有就是利用中間件進(jìn)行異步處理。中間件的由來在Redux官網(wǎng)上已經(jīng)解釋的很清楚了,我們這里只是使用它們。
Redux 中間件的樣式如下:
const logger = store => next => action => {// 這里面就是我們這個中間件的要做的事件,由于把store, action 都傳入來進(jìn)來,我們可以對action進(jìn)行操作,也可以調(diào)用store暴露的 api. }它接受一個store作為參數(shù),然后返回一個函數(shù),函數(shù)接受next作為參數(shù),還是返回一個函數(shù),函數(shù)再接受一個參數(shù)action, 然后,我們在這個函數(shù)體內(nèi)寫代碼,表示這個中間件要做的事情,es6的箭頭函數(shù)可能不直觀,我們用es5重寫一下,就以官網(wǎng)中的logger為例
// es6 箭頭函數(shù)樣式 const logger = store => next => action => {console.log('dispatching', action);let result = next(action);console.log('next state', store.getState());return result; } // es5 普通樣式 const logger = function(store) {return function(next) {return function(action) {console.log('dispatching', action);let result = next(action);console.log('next state', store.getState());return result;}} }在這個logger中間件(函數(shù))中,第一句console.log(),輸出我們發(fā)送的action, 從而記錄下action. 第二句next, 調(diào)用next 函數(shù)發(fā)送action, 這個next函數(shù),就是store.dispath() 方法的封裝。next就是store.dispatch action后,我們就會獲取的新的狀態(tài),所以第三句console.log (store.getState())就可以獲取新的狀態(tài)并記錄。最后一句return,?主要的是因為中間件可以鏈接起來調(diào)用,一個接一個,所以要return, 把它傳入到下一個中間件。
新建一個項目來體驗一下中間件。由于后面還要使用異步,我們就一起寫了。項目非常簡單,頁面上有一個按鈕,點擊按鈕時,我們從randomuser.me網(wǎng)站上隨機(jī)獲取一個用戶,并進(jìn)行展示,這很明顯涉及到了異步請求。 直接把redux介紹中的使用npm和ES6的項目復(fù)制過來,index.html中的body修改如下:
<body><div class="container"><button class='btn btn-blue' id="getUser">New Random User</button> <h2 id="status"></h2> <h2 id="name"></h2><h2 id="email"></h2><h2 id="gender"></h2></div><script src='./bundle.js'></script></body>counter.css 修改如下:
body {padding: 40px;font-family: "helvetica neue", sans-serif;}.container {width: 600px;margin: auto;color: black;padding: 20px;text-align: center; }.container h1 {margin: 0;padding: 20px;font-size: 48px; }.container .btn {border: 0;padding: 15px;margin: 10px;width: 40%;font-size: 15px;outline: none;border-radius: 3px;color: #FFF;font-weight: bold; }.btn.btn-blue {background-color: #55acee;box-shadow: 0px 5px 0px 0px #3C93D5; } .pending {color: #55acee; }.error {color: #d84a4a; }.success {color: #2ecc71; }npm run dev 啟動項目后,我們頁面中就只有一個按鈕
現(xiàn)在寫index.js文件,首先我們要定義state, reducer來創(chuàng)建store. 對于異步請求來說,它至少有3種狀態(tài),請求中的狀態(tài),請求成功時的狀態(tài),請求失敗時的狀態(tài)。所以首先我們要記錄這個狀態(tài)成用戶提示,為了成功和失敗進(jìn)行樣式區(qū)分,還有一個樣式狀態(tài),成功之后,我們獲取到用戶的信息,所以要有一個user信息。
// state 狀態(tài) const initalState = {user: { // 記錄個人信息,每個人都有姓名,年齡,郵箱,所以它是一個對象。name: '',email: '',gender: ''},status: '', // 記錄請求狀態(tài)。 statusClass: '' // 請求狀態(tài)成功或失敗的樣式標(biāo)識 }對于三種狀態(tài),它肯定會有三個action進(jìn)行對應(yīng),請求中的狀態(tài),我們可以發(fā)送一個?GET_USER, 請求成功的狀態(tài),我們可以發(fā)送USER_RECIEVED, 請求失敗的狀態(tài),我們可以發(fā)送ERROR,相應(yīng)地,recuder中就要處理這3個type
當(dāng)我們發(fā)送GET_USER action時,那么請求狀態(tài)的變量status應(yīng)為pending, 同是statusClass至為pending ,所以reducer這個分支,只需要要改變這個狀態(tài)為{user:{}, status: 'pending', statusClass: 'pending"}
當(dāng)USER_RECIEVED 時,我們就獲取的user信息,同時state 就變成了?User Recieved 狀態(tài),這個分支可能如下:
{user: { name: 'sam',email: '105@gmail.com',gender: 'male'},status: 'User reciever', statusClass: 'success' }當(dāng)ERROR 時,我們也是改變狀態(tài),就可以了,和GET_USER ?一樣。整 個reducer代碼如下:
// REDCUER function userReducer(state=initalState, action) {const user = {name: '',email: '',gender: ''};switch (action.type) {case 'GET_USER':return {...state, status: 'Pending...', statusClass: 'pending'}case 'USER_RECIEVED':user.name = `${action.payload[0].name.first} ${action.payload[0].name.last}`user.email = action.payload[0].email;user.gender = action.payload[0].gender;return {...state, user, status: 'User Recieved', statusClass: 'success'}case 'ERROR':return {...state, status: `${action.payload.message}`, statusClass: 'error'}default:return state} }這時我們就可以創(chuàng)建store了,創(chuàng)建store的時候,它可以接受第二個參數(shù),就是中間件,不過要用到Redux提件的
applyMiddleware 函數(shù),所有用到的中間件依次作為參數(shù),傳遞給它,這時使用一下logger // STORE接受applyMiddlerWare 參數(shù),從而使用中間件,把用到的中間件傳入到applyMiddlerWare函數(shù)中 const store = createStore(userReducer, applyMiddleware(logger));這時我們點擊按鈕發(fā)送一個?GET_USER試一試
// ACTIONS document.getElementById('getUser').addEventListener('click', function () {store.dispatch({type:"GET_USER"}); })可以看到控制臺上對我們的action 和state 進(jìn)行的記錄, 中間件起了作用。
對于logger這種常用的中間件,網(wǎng)上已經(jīng)有第三方庫了,就是redux-logger. npm install redux-logger --save?安裝到我們的項目中, 然后在index.js文件引入,import {createLogger} from 'redux-logger'; 然后applyMiddleware(createLogger()) 就可以了。
現(xiàn)在再做異步請求, 有兩個中間件可以做異步action:redux-thunk 和redux-promise, 先使用redux-thunk, 發(fā)送異步請求用到了 axios; ?npm install redux-thunk axios --save 安裝它們。
使用thunk做異步action, dispatch可以接受一個函數(shù)作為參數(shù),這個函數(shù)接受dispatch作為參數(shù),在這個函數(shù)內(nèi)部我們可以發(fā)送異步請求,獲取結(jié)果后,再發(fā)送action
// 使用異步ACTIONS document.getElementById('getUser').addEventListener('click', function () {store.dispatch(dispatch => {// 請求中的actiondispatch({type: 'GET_USER'});// do the xhr requestaxios.get('https://randomuser.me/api/')// 請求成功action.then(response => {dispatch({type: 'USER_RECIEVED', payload: response.data.results})})// 失敗的action.catch(error => {dispatch({ type: 'ERROR', payload: error})})});})最后寫一個render函數(shù),把獲取到的狀態(tài)渲染到頁面上
// 獲取元素 const nameEl = document.getElementById('name'); const emailEl = document.getElementById('email'); const genderEl = document.getElementById('gender'); const statusEl = document.getElementById('status');function render() {const state = store.getState()nameEl.innerHTML = state.user.name;emailEl.innerHTML = state.user.email;genderEl.innerHTML = state.user.gender;statusEl.innerHTML = state.status;statusEl.className = state.statusClass; }render() store.subscribe(render)
整 個index.js文件如下:
import { createStore, applyMiddleware } from 'redux'; // 引入applyMiddleware 使用中間件 import {createLogger} from 'redux-logger'; // 使用第三方logger import thunk from 'redux-thunk'; // 使用redux-thunk 操作異步action import axios from 'axios'; // 使用axios發(fā)送異步請求// state 狀態(tài) const initalState = {user: { // 記錄個人信息,每個人都有姓名,年齡,郵箱,所以它是一個對象。name: '',email: '',gender: ''},status: '', // 記錄請求狀態(tài)。 statusClass: '' // 請求狀態(tài) }// REDCUER function userReducer(state=initalState, action) {const user = {name: '',email: '',gender: ''};switch (action.type) {case 'GET_USER':return {...state, status: 'Pending...', statusClass: 'pending'}break;case 'USER_RECIEVED':user.name = `${action.payload[0].name.first} ${action.payload[0].name.last}`user.email = action.payload[0].email;user.gender = action.payload[0].gender;return {...state, user, status: 'User Recieved', statusClass: 'success'}break;case 'ERROR':return {...state, status: `${action.payload.message}`, statusClass: 'error'}default:return state} }// STORE, 使用了第三方 logger 和thunk中間件 const store = createStore(userReducer, applyMiddleware(createLogger(), thunk)); // 獲取元素 const nameEl = document.getElementById('name'); const emailEl = document.getElementById('email'); const genderEl = document.getElementById('gender'); const statusEl = document.getElementById('status');// 進(jìn)行渲染 function render() {const state = store.getState()nameEl.innerHTML = state.user.name;emailEl.innerHTML = state.user.email;genderEl.innerHTML = state.user.gender;statusEl.innerHTML = state.status;statusEl.className = state.statusClass; }render() store.subscribe(render)// 使用異步ACTIONS document.getElementById('getUser').addEventListener('click', function () {store.dispatch(dispatch => {// 請求中的actiondispatch({type: 'GET_USER'});// do the xhr requestaxios.get('https://randomuser.me/api/')// 請求成功action.then(response => {dispatch({type: 'USER_RECIEVED', payload: response.data.results})})// 失敗的action.catch(error => {dispatch({ type: 'ERROR', payload: error})})});})當(dāng)點擊頁面上的按鈕時,先看到的pend..., 然后就是獲取人物信息
?
現(xiàn)在再使用redux-promise-middleware來發(fā)送異步action. ?redux-promise-middleware中間件充分利用了promise的特性, 因為promise有三種狀態(tài),pending ,fullfilled, reject, ?正好對應(yīng)著異步請求的三種狀態(tài), 我們只要發(fā)送一個action, 自動會獲取到三個action,把pending, fullfilled, reject加到我們發(fā)送的action 后面形成。npm install?redux-promise-middleware --save 安裝它,然后import promise from?redux-promise-middleware引入。在異步action中我們只發(fā)送一個action, 比如FETCH_USER;
// 使用異步ACTIONS document.getElementById('getUser').addEventListener('click', function () {store.dispatch({type: 'FETCH_USER',payload: axios.get("https://randomuser.me/api/")}); })雖然只有一個action,它實際上發(fā)送了三個action: ?FETCH_USER_PENDING, ?FETCH_USER_FULFILLED, ?FETCH_USER_REJECTED, ?你可以看到就是我們發(fā)送的action, 后面加上promise的三種狀態(tài)形成: reducer修改如下:
// REDCUER function userReducer(state=initalState, action) {const user = {name: '',email: '',gender: ''};switch (action.type) {case 'FETCH_USER_PENDING':return {...state, sendingRequest: true, status: 'Pending...', statusClass: 'pending'}break;case 'FETCH_USER_FULFILLED':user.name = `${action.payload.data.results[0].name.first} ${action.payload.data.results[0].name.last}`user.email = action.payload.data.results[0].email;user.gender = action.payload.data.results[0].gender;return {...state, sendingRequest: false, user, status: 'User Recieved', statusClass: 'success'}break;case 'FETCH_USER_REJECTED':return {...state, sendingRequest: false, status: `${action.payload.message}`, statusClass: 'error'}default:return state} }現(xiàn)在點擊按鈕,也可以獲取到用戶信息。整個代碼如下:
import { createStore, applyMiddleware } from 'redux'; // 引入applyMiddleware 使用中間件 import {createLogger} from 'redux-logger'; // 使用第三方logger import promise from 'redux-promise-middleware'; import axios from 'axios'; // 使用axios發(fā)送異步請求// state 狀態(tài) const initalState = {user: { // 記錄個人信息,每個人都有姓名,年齡,郵箱,所以它是一個對象。name: '',email: '',gender: ''},status: '', // 記錄請求狀態(tài)。 statusClass: '' // 請求狀態(tài) }// REDCUER function userReducer(state=initalState, action) {const user = {name: '',email: '',gender: ''};switch (action.type) {case 'FETCH_USER_PENDING':return {...state, sendingRequest: true, status: 'Pending...', statusClass: 'pending'}break;case 'FETCH_USER_FULFILLED':user.name = `${action.payload.data.results[0].name.first} ${action.payload.data.results[0].name.last}`user.email = action.payload.data.results[0].email;user.gender = action.payload.data.results[0].gender;return {...state, sendingRequest: false, user, status: 'User Recieved', statusClass: 'success'}break;case 'FETCH_USER_REJECTED':return {...state, sendingRequest: false, status: `${action.payload.message}`, statusClass: 'error'}default:return state} }// STORE const store = createStore(userReducer, applyMiddleware(createLogger(), promise())); // 獲取元素 const nameEl = document.getElementById('name'); const emailEl = document.getElementById('email'); const genderEl = document.getElementById('gender'); const statusEl = document.getElementById('status');// 進(jìn)行渲染 function render() {const state = store.getState()nameEl.innerHTML = state.user.name;emailEl.innerHTML = state.user.email;genderEl.innerHTML = state.user.gender;statusEl.innerHTML = state.status;statusEl.className = state.statusClass; }render() store.subscribe(render)// 使用異步ACTIONS document.getElementById('getUser').addEventListener('click', function () {store.dispatch({type: 'FETCH_USER',payload: axios.get("https://randomuser.me/api/")}); })?
?
轉(zhuǎn)載于:https://www.cnblogs.com/SamWeb/p/7899634.html
總結(jié)
以上是生活随笔為你收集整理的Redux学习(2) ----- 异步和中间件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JQuery 实现 锚点跳转
- 下一篇: PHP命令空间namespace及use