· 6 years ago · Mar 23, 2020, 01:40 PM
1import {useState, useCallback, useMemo, useContext} from 'react';
2import api from 'api/';
3import {Store} from 'store/database';
4
5/**
6 * Binds a request to make to the server using the fetch api and the loading, error, and fetching logic
7 * @param path - the path of the api call
8 * @param params - default params to use on fetch api
9 * @param setLoading - the setter for loading
10 * @param setError - the setter for error
11 * @param statusCode - the code that matches success, default is 200
12 * @returns {Promise<*>} result of response.json() or error
13 */
14const makeRequestBinding = (setLoading, setError, statusCode = 200, defaultParams) => async (path, params = {}) => {
15
16 try {
17 // set loading and clean error
18 setLoading(true);
19 setError(null);
20
21 const mergedParams = {...defaultParams, ...params};
22
23 // do the api call
24 const response = await api.fetch(path, mergedParams);
25
26 //check status code
27 if (response.status === statusCode) {
28 return await response.json(); //status code match with default 200 or custom code
29 } else {
30 const e = await response.json();
31 setError(e); //no match for status code
32 throw new Error(JSON.stringify(e))
33 }
34 } catch (e) {
35 setError(e.message); //error on fetch or json
36 console.log(e)
37 throw new Error(e)
38 } finally {
39 setLoading(false) //finally set loading falt
40 }
41};
42
43
44const defaultTransformFunction = (data) => Array.isArray(data.data) ? data.data.reduce(function (map, entity) {
45 map[entity.id] = entity;
46 return map;
47}, {}) : {[data.id]: data};
48
49/**
50 * Default config for the entity
51 * @type {{deleteDataStatusCode: number, putDataStatusCode: number, fetchDataStatusCode: number, patchDataStatusCode: number}}
52 */
53const defaultConfig = {
54 transformFunction: defaultTransformFunction,
55 fetchDataStatusCode: 200,
56 getDetailDataStatusCode: 200,
57 patchDataStatusCode: 200,
58 putDataStatusCode: 200,
59 deleteDataStatusCode: 200,
60};
61
62/**
63 * Creates the base for the entity
64 * @param config - config like {
65 fetchDataStatusCode: 200,
66 getDetailDataStatusCode: 200,
67 patchDataStatusCode: 200,
68 putDataStatusCode: 200,
69 deleteDataStatusCode: 200,
70}
71 * @returns {Promise<{data: {}, putting: boolean, deletingError: unknown, fetching: boolean, patchingError: unknown, patching: boolean, fetchingError: unknown, patchData: *, fetchData: *, deleting: boolean, deleteData: *, putData: *, puttingError: unknown}>}
72 */
73const useEntity = (entityName, config = defaultConfig) => {
74
75 const {database, dispatch} = useContext(Store);
76 console.log("TIAGO", database);
77
78 // fetching page or alot of data
79 const [fetching, setFetching] = useState(null);
80 const [fetchingError, setFetchingError] = useState(null);
81
82 // to get only one
83 const [getting, setGetting] = useState(null);
84 const [gettingError, setGettingError] = useState(null);
85
86 // to edit/patch entity
87 const [patching, setPatching] = useState(null);
88 const [patchingError, setPatchingError] = useState(null);
89
90 // to create entity
91 const [putting, setPutting] = useState(null);
92 const [puttingError, setPuttingError] = useState(null);
93
94 // to create entity
95 const [deleting, setDeleting] = useState(null);
96 const [deletingError, setDeletingError] = useState(null);
97
98 const fetchDataCall = useMemo(() => makeRequestBinding(setFetching, setFetchingError, defaultConfig.fetchDataStatusCode, {method: 'GET'}), [setFetching, setFetchingError]);
99 const detailGetDataCall = useMemo(() => makeRequestBinding(setGetting, setGettingError, defaultConfig.getDetailDataStatusCode, {method: 'GET'}), [setGetting, setGettingError]);
100 const patchDataCall = useMemo(() => makeRequestBinding(setPatching, setPatchingError, defaultConfig.patchDataStatusCode, {method: 'PATCH'}), [setPatching, setPatchingError]);
101 const putDataCall = useMemo(() => makeRequestBinding(setPutting, setPuttingError, defaultConfig.putDataStatusCode, {method: 'PUT'}), [setPutting, setPuttingError]);
102 const deleteDataCall = useMemo(() => makeRequestBinding(setDeleting, setDeletingError, defaultConfig.deleteDataStatusCode, {method: 'DELETE'}), [setDeleting, setDeletingError]);
103
104 const fetchData = useCallback(async (url, params) => {
105
106 try {
107 const newData = await fetchDataCall(url, params);
108 const transformedNewData = await config.transformFunction(await newData);
109
110
111 const test1 = {};
112
113 for (let key in transformedNewData) {
114 test1[key] = {test1: 1, ...transformedNewData[key]}
115 }
116
117
118 await dispatch({
119 type: "ADD_MULTIPLE",
120 entityName,
121 data: test1
122 });
123
124 const test2 = {};
125
126 for (let key in transformedNewData) {
127 test2[key] = {test2: 2, ...transformedNewData[key]}
128 }
129
130
131 await dispatch({
132 type: "ADD_MULTIPLE",
133 entityName,
134 data: test2
135 });
136 return newData; //always return the answer, we never know when they need it
137 } catch (e) {
138 throw (e);
139 }
140
141 }, []);
142
143
144 const detailGetData = useCallback(async (url, params) => {
145
146 try {
147 const newData = await detailGetDataCall(url, params);
148 const transformedNewData = await config.transformFunction(await newData);
149
150 await dispatch({
151 type: "ADD_SINGLE",
152 entityName,
153 data: transformedNewData
154 });
155
156 return newData; //always return the answer, we never know when they need it
157 } catch (e) {
158 console.log(e);
159 }
160
161 }, []);
162
163 const patchData = useCallback(async (url, params) => {
164
165 try {
166 const newData = await patchDataCall(url, params);
167 const transformedNewData = await config.transformFunction(await newData);
168 await dispatch({
169 type: "PATCH",
170 entityName,
171 data: transformedNewData
172 });
173 return newData; //always return the answer, we never know when they need it
174 } catch (e) {
175 console.log(e);
176 }
177
178 }, []);
179
180 const putData = useCallback(async (url, params) => {
181
182 try {
183 const newData = await putDataCall(url, params);
184 const transformedNewData = await config.transformFunction(await newData);
185 await dispatch({
186 type: "CREATE",
187 entityName,
188 data: transformedNewData
189 });
190 return newData; //always return the answer, we never know when they need it
191 } catch (e) {
192 console.log(e);
193 }
194
195 }, []);
196
197 const deleteData = useCallback(async (url, params) => {
198
199 try {
200 const deletedData = await fetchDataCall(url, params);
201 await dispatch({
202 type: "DELETE",
203 entityName,
204 data: deletedData
205 });
206
207 } catch (e) {
208 console.log(e);
209 }
210
211 }, []);
212
213 return {
214 data: database[entityName], fetching, fetchingError, fetchData,
215 getting, gettingError, detailGetData,
216 patching, patchingError, patchData,
217 putting, puttingError, putData,
218 deleting, deletingError, deleteData
219 };
220};
221
222export default useEntity;