UNPKG

2.87 kBJavaScriptView Raw
1/// <reference path='../typings/index.d.ts' />
2import {
3 call,
4 cancel,
5 fork,
6 take,
7 put,
8 all,
9 takeLatest,
10 select,
11} from "redux-saga/effects";
12import { normalize } from "normalizr";
13import {
14 isRequest,
15 isRefresh,
16 successOf,
17 failureOf,
18 requestOf,
19} from "./actions";
20import { humps } from "./lib";
21import { makeStateSelector } from "./selector";
22import { Apis } from "./apis";
23
24// hooks
25const sagaHooks = {
26 beforeRequest: null,
27 afterRequest: null,
28};
29
30export function registerHooks(hooks = {}) {
31 sagaHooks.beforeRequest = hooks.beforeRequest || null;
32 sagaHooks.afterRequest = hooks.afterRequest || null;
33}
34
35/**
36 *
37 * @param {string} key
38 * @param {import("@36node/redux-api").Action} action
39 * @param {import("@36node/redux-api").Meta} meta
40 */
41export function* callAPI(key, action, meta) {
42 if (!key) return;
43 if (!action) return;
44
45 const { payload } = action;
46 const api = Apis.get(key);
47
48 if (!api) {
49 throw new Error(`redux-api: api ${key} should be registered`);
50 }
51
52 const endpoint = api.endpoint;
53 if (!endpoint) {
54 throw new Error(`redux-api: api ${key} should has endpoint`);
55 }
56
57 const schema = api.schema;
58
59 if (sagaHooks.beforeRequest) {
60 const passed = yield call(sagaHooks.beforeRequest, key, action, api);
61 // if before request hook return false, will cancel current request
62 if (!passed) {
63 return;
64 }
65 }
66
67 let epResult;
68 let epError;
69 try {
70 epResult = (yield call(endpoint, payload)) || {};
71 const { body = {}, headers = {} } = epResult;
72 const data = schema ? normalize(body, schema) : { result: body };
73
74 yield put({
75 type: successOf(key),
76 payload: { ...data, ...humps(headers) },
77 key,
78 meta,
79 });
80 } catch (err) {
81 yield put({
82 type: failureOf(key),
83 error: err,
84 key,
85 meta,
86 });
87 epError = err;
88 }
89
90 if (sagaHooks.afterRequest) {
91 yield call(sagaHooks.afterRequest, key, action, api, epResult, epError);
92 }
93}
94
95export function* watchRequest() {
96 const tasks = {};
97 while (true) {
98 const action = yield take(isRequest);
99 const { meta = {}, key } = action;
100 if (tasks[key]) yield cancel(tasks[key]); //cancel last task of key
101 tasks[key] = yield fork(callAPI, key, action, meta);
102 }
103}
104
105export function* watchRefresh(action) {
106 // const action = yield take(isRefresh);
107 const { key } = action;
108 const api = Apis.get(key);
109 if (!api) {
110 throw new Error(`redux-api: api ${key} should be registered`);
111 }
112 const stateSelector = makeStateSelector(api.reduxPath);
113 const apiState = yield select(stateSelector);
114
115 // put last request action
116 yield put({
117 type: requestOf(key),
118 key,
119 payload: apiState.request,
120 meta: apiState.meta,
121 });
122 // while (true) {
123 // }
124}
125
126export function* watchApis() {
127 yield all([fork(watchRequest), takeLatest(isRefresh, watchRefresh)]);
128}