# 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/)