成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

react+redux+router異步數(shù)據(jù)獲取教程

Arno / 3089人閱讀

摘要:寫法一,返回值是一個(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、actionreducer、dispatch。就算看的懂圖,也不一定能coding出來。

不過我總算先搞定了Redux

keywords

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的,導(dǎo)入store

    {...}
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.receiveData
With 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);

        
           
               {routes}
           
        
Async

類似 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是引入Middleware
import 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 組件的更新。

// 容器組件的代碼
//    
//      All
//    

const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}
mapDispatchToProps()

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

相關(guān)文章

  • dva框架使用詳解及Demo教程

    摘要:框架的使用詳解及教程在前段時(shí)間,我們也學(xué)習(xí)講解過框架的基本使用,但是有很多同學(xué)在交流群里給我的反饋信息說,框架理解上有難度,看了之后還是一臉懵逼不知道如何下手,很多同學(xué)就轉(zhuǎn)向選擇使用框架。 dva框架的使用詳解及Demo教程 在前段時(shí)間,我們也學(xué)習(xí)講解過Redux框架的基本使用,但是有很多同學(xué)在交流群里給我的反饋信息說,redux框架理解上有難度,看了之后還是一臉懵逼不知道如何下手,很...

    bergwhite 評(píng)論0 收藏0
  • 一些基于React、Vue、Node.js、MongoDB技術(shù)棧的實(shí)踐項(xiàng)目

    摘要:利用中間件實(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)單,有的...

    tangr206 評(píng)論0 收藏0
  • 前端面試題總結(jié)(js、html、小程序、React、ES6、Vue、算法、全棧熱門視頻資源)

    摘要:并總結(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í)前端工程師快...

    pumpkin9 評(píng)論0 收藏0
  • 前端面試題總結(jié)(js、html、小程序、React、ES6、Vue、算法、全棧熱門視頻資源)

    摘要:并總結(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í)前端工程師快...

    Carson 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<