# Middleware/api ## 前情提要 > Redux 的 middleware 是介於 store.dispatch 和 reducers 之間的中間人。 更精準地說,我們 call 的 store.dispatch() 其實是由一層一層的 middleware 所組成,到最後一層才會進到 reducer。 > ^2^ 透過 middleware, 我們可以把 async api request 的行為抽出來,放在同一個 middleware 裡面。意思是說,**我們在需要發 api request 的地方 dispatch 一種特定的 action,然後在 middleware 去攔截這種 action 來發相對應的 api request。** > ^1^ Redux middleware 一共嵌套三層函數,分别傳遞**store、next、action**三個參數。 是在一層函數中傳遞一個參數,共三層。 > - next 因為middlewate需要多個首尾相連,因此對next進行一層層的"加工",所以next需獨立一層。 > - store middleware頂層放上store,因為要用store的dispatch和getState两個方法。 > - action 是因為middleware封裝多層,為了要用高級的dispatch方法,就得使用action。 > ^3^ 我們在真正需要發 request 的地方不需要特別處理 async 的邏輯,而只要 dispatch 出一個帶有 CALL_API 的 action 就行了 ### FaceAI應用 ``` // middleware/api.js // 定義CALL_API // Action key that carries API call info interpreted by this Redux middleware. export const CALL_API = 'Call API' ``` ``` // 1. 用store => next => action => {} 實現三層函數嵌套。 最后指向的那个{}寫關於dispatch的,但需要返回next,给下一個middleware使用。 // 2. middleware 會攔截有 CALL_API 這個 key 的 action, 然後根據裡面的data及執行callApi function去決定怎麼發送 api request。 // A Redux middleware that interprets actions with CALL_API info specified. // Performs the call and promises when such actions are dispatched. export default store => next => async action => { /* 1 const callAPI = action[CALL_API] /* 2 if (typeof callAPI === 'undefined') { return next(action) } let { endpoint } = callAPI const { schema, type, suppressError, suppressLoading } = callAPI const types = [type, `${type}_SUCCESS`, `${type}_FAILURE`] ... const response = await callApi(callAPI, schema) /* 4. callApi(action, schema) const { meta, data, ...rest } = response ... } ``` ``` // actions/faceTypes.js // 3. dispatch 出一個帶有 CALL_API 的 action 就行了 export const addFaceType = payload => ({ /* 搭配2 [CALL_API]: { /* 3 type: ADD_FACE_TYPE, schema: Schemas.FACE_TYPE, payload, method: 'POST', endpoint: '/v1/face-types' } }) ``` ``` // middleware/api.js // callApi function /* 4 const callApi = (action, schema) => { const { endpoint, method, fetchType = 'json', payload } = action ... return jsonFetch(url, config, schema) } // jsonFetch function /* 5 const jsonFetch = async (url, config, schema) => { ... return schema /* 取得data ? { meta, data: { key: Array.isArray(schema) ? schema[0]._key : schema._key, ...normalize(data, schema) /* 正規化 }, ...rest } : { meta, data, ...rest } } ``` ### normalize [筆記](https://hackmd.io/@JtVWZvXpSdaEQk8pzSQTlA/rySzz_J7O) ### 參考文件 [- 參考文件](https://blog.mz026.rocks/20151129/hello-redux-1-3-redux-intro) [- 參考文件](https://www.liuyiqi.cn/2016/02/02/r2-middleware/)