@civet/core 1.2.7 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -2
- package/lib/AbortSignal.js +4 -5
- package/lib/ChannelNotifier.js +4 -5
- package/lib/ConfigProvider.js +10 -10
- package/lib/DataProvider.js +7 -11
- package/lib/Meta.js +6 -7
- package/lib/Notifier.js +4 -5
- package/lib/Resource.js +35 -27
- package/lib/compose.js +1 -2
- package/lib/context.js +8 -13
- package/lib/createPlugin.js +10 -13
- package/lib/index.js +8 -11
- package/lib/uniqueIdentifier.js +2 -3
- package/lib/useResource.js +227 -151
- package/package.json +29 -25
- package/src/{ConfigProvider.js → ConfigProvider.jsx} +3 -4
- package/src/DataProvider.js +1 -2
- package/src/{Resource.js → Resource.jsx} +16 -15
- package/src/context.js +5 -5
- package/src/createPlugin.js +0 -1
- package/src/index.js +3 -3
- package/src/useResource.js +224 -150
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
import { ResourceContext } from './context';
|
|
5
2
|
import { dataProviderPropType } from './DataProvider';
|
|
3
|
+
import { ResourceContext } from './context';
|
|
6
4
|
import useResource from './useResource';
|
|
7
5
|
|
|
8
6
|
const propTypes = {
|
|
9
|
-
/** DataProvider to be used for requests */
|
|
7
|
+
/** DataProvider to be used for requests - must not be changed */
|
|
10
8
|
dataProvider: dataProviderPropType,
|
|
11
9
|
/** Resource name */
|
|
12
10
|
name: PropTypes.string.isRequired,
|
|
13
|
-
/** Query */
|
|
11
|
+
/** Query instructions */
|
|
14
12
|
query: PropTypes.any,
|
|
15
|
-
/**
|
|
13
|
+
/** Disables fetching data, resulting in an empty data array */
|
|
16
14
|
empty: PropTypes.bool,
|
|
17
|
-
/**
|
|
15
|
+
/** Query options for requests */
|
|
18
16
|
options: PropTypes.object,
|
|
19
|
-
/** Whether stale data should be retained during the next request - this only applies if
|
|
17
|
+
/** Whether stale data should be retained during the next request - this only applies if name did not change, unless set to "very" */
|
|
20
18
|
persistent: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['very'])]),
|
|
21
19
|
children: PropTypes.node,
|
|
22
20
|
};
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
23
|
+
* Provides data based on the given request details and DataProvider.
|
|
24
|
+
* Context provider for the ResourceContext.
|
|
25
|
+
*
|
|
26
|
+
* Necessary configuration that is not directly specified is taken from the ConfigContext.
|
|
27
|
+
*
|
|
28
|
+
* The provided DataProvider must not be changed.
|
|
28
29
|
*/
|
|
29
30
|
function Resource({ dataProvider, name, query, empty, options, persistent, children, ...rest }) {
|
|
30
|
-
const context = useResource({ dataProvider, name, query, empty, options, persistent });
|
|
31
|
+
const context = useResource({ dataProvider, name, query, empty, options, persistent, ...rest });
|
|
31
32
|
|
|
32
33
|
return context.dataProvider.uiPlugins.reduceRight(
|
|
33
|
-
(next, Plugin) =>
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
(next, Plugin) =>
|
|
35
|
+
// eslint-disable-next-line react/display-name
|
|
36
|
+
(result) => (
|
|
36
37
|
<Plugin {...rest} context={result}>
|
|
37
38
|
{next}
|
|
38
39
|
</Plugin>
|
package/src/context.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
2
|
|
|
3
3
|
const noop = () => {};
|
|
4
4
|
|
|
5
|
-
export const ConfigContext =
|
|
5
|
+
export const ConfigContext = createContext({});
|
|
6
6
|
ConfigContext.displayName = 'ConfigContext';
|
|
7
|
-
export const useConfigContext = () =>
|
|
7
|
+
export const useConfigContext = () => useContext(ConfigContext);
|
|
8
8
|
|
|
9
|
-
export const ResourceContext =
|
|
9
|
+
export const ResourceContext = createContext({ data: [], notify: noop });
|
|
10
10
|
ResourceContext.displayName = 'ResourceContext';
|
|
11
|
-
export const useResourceContext = () =>
|
|
11
|
+
export const useResourceContext = () => useContext(ResourceContext);
|
package/src/createPlugin.js
CHANGED
|
@@ -4,7 +4,6 @@ function createPlugin(plugin) {
|
|
|
4
4
|
if (typeof plugin !== 'function') throw new Error('No valid plugin definition specified');
|
|
5
5
|
return (dataProviderClass) => {
|
|
6
6
|
if (!Object.prototype.isPrototypeOf.call(DataProvider, dataProviderClass)) {
|
|
7
|
-
// eslint-disable-next-line no-console
|
|
8
7
|
console.error(
|
|
9
8
|
'A plugin should be given a derivative of the DataProvider class as its first parameter',
|
|
10
9
|
);
|
package/src/index.js
CHANGED
|
@@ -4,12 +4,12 @@ export const { Consumer: ConfigConsumer } = ConfigContext;
|
|
|
4
4
|
export const { Provider: ResourceProvider, Consumer: ResourceConsumer } = ResourceContext;
|
|
5
5
|
export { default as AbortSignal } from './AbortSignal';
|
|
6
6
|
export { default as ChannelNotifier } from './ChannelNotifier';
|
|
7
|
-
export { default as compose } from './compose';
|
|
8
7
|
export { default as ConfigProvider } from './ConfigProvider';
|
|
9
|
-
export { default as
|
|
10
|
-
export { dataProviderPropType, default as DataProvider, isDataProvider } from './DataProvider';
|
|
8
|
+
export { default as DataProvider, dataProviderPropType, isDataProvider } from './DataProvider';
|
|
11
9
|
export { default as Meta } from './Meta';
|
|
12
10
|
export { default as Notifier } from './Notifier';
|
|
13
11
|
export { default as Resource } from './Resource';
|
|
12
|
+
export { default as compose } from './compose';
|
|
13
|
+
export { default as createPlugin } from './createPlugin';
|
|
14
14
|
export { default as useResource } from './useResource';
|
|
15
15
|
export { useConfigContext, useResourceContext };
|
package/src/useResource.js
CHANGED
|
@@ -1,55 +1,198 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
|
3
2
|
import AbortSignal from './AbortSignal';
|
|
4
3
|
import Meta from './Meta';
|
|
5
4
|
import { useConfigContext } from './context';
|
|
6
5
|
import uniqueIdentifier from './uniqueIdentifier';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
|
-
*
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
* Appends a new requestInstruction to the state, causing the resource to fetch data.
|
|
9
|
+
**/
|
|
10
|
+
function createRequestInstruction(state) {
|
|
11
|
+
return {
|
|
12
|
+
...state,
|
|
13
|
+
requestInstruction: {
|
|
14
|
+
dataProvider: state.dataProvider,
|
|
15
|
+
requestDetails: state.requestDetails,
|
|
16
|
+
request: state.request,
|
|
17
|
+
revision: state.revision,
|
|
18
|
+
value: state.value,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* State reducer for the resource.
|
|
25
|
+
*/
|
|
26
|
+
function reducer(state, action) {
|
|
27
|
+
switch (action.type) {
|
|
28
|
+
// Creates a new request and instructs the resource to fetch data.
|
|
29
|
+
case 'next-request': {
|
|
30
|
+
const { requestDetails: nextRequestDetails, persistent: nextPersistent } = action;
|
|
31
|
+
const nextRequest = uniqueIdentifier(state.request);
|
|
32
|
+
const nextRevision = uniqueIdentifier(state.revision);
|
|
33
|
+
let isPersistent = false;
|
|
34
|
+
if (state.persistent === 'very' && nextPersistent === 'very') {
|
|
35
|
+
isPersistent = 'very';
|
|
36
|
+
} else if (state.persistent && nextPersistent) {
|
|
37
|
+
isPersistent = true;
|
|
38
|
+
}
|
|
39
|
+
const shouldValuePersist =
|
|
40
|
+
!nextRequestDetails.empty &&
|
|
41
|
+
state.dataProvider.shouldPersist(
|
|
42
|
+
nextRequestDetails,
|
|
43
|
+
state.requestDetails,
|
|
44
|
+
isPersistent,
|
|
45
|
+
state.value,
|
|
46
|
+
);
|
|
47
|
+
return createRequestInstruction({
|
|
48
|
+
dataProvider: state.dataProvider,
|
|
49
|
+
requestDetails: nextRequestDetails,
|
|
50
|
+
request: nextRequest,
|
|
51
|
+
revision: nextRevision,
|
|
52
|
+
isLoading: !nextRequestDetails.empty,
|
|
53
|
+
value: shouldValuePersist
|
|
54
|
+
? state.value
|
|
55
|
+
: {
|
|
56
|
+
name: nextRequestDetails.name,
|
|
57
|
+
query: nextRequestDetails.query,
|
|
58
|
+
options: nextRequestDetails.options,
|
|
59
|
+
request: nextRequest,
|
|
60
|
+
revision: nextRevision,
|
|
61
|
+
data: [],
|
|
62
|
+
meta: {},
|
|
63
|
+
error: undefined,
|
|
64
|
+
isEmpty: !!nextRequestDetails.empty,
|
|
65
|
+
isIncomplete: !nextRequestDetails.empty,
|
|
66
|
+
isInitial: !nextRequestDetails.empty,
|
|
67
|
+
},
|
|
68
|
+
persistent: nextPersistent,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Creates a new revision for the current request and instructs the resource to fetch data.
|
|
73
|
+
case 'next-revision': {
|
|
74
|
+
const { notify } = action;
|
|
75
|
+
const nextRevision = uniqueIdentifier(state.revision);
|
|
76
|
+
notify({ request: state.request, revison: nextRevision });
|
|
77
|
+
return createRequestInstruction({
|
|
78
|
+
...state,
|
|
79
|
+
revision: nextRevision,
|
|
80
|
+
isLoading: !state.requestDetails.empty,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Sets a new persistence level.
|
|
85
|
+
case 'set-persistence': {
|
|
86
|
+
const { persistent: nextPersistent } = action;
|
|
87
|
+
return {
|
|
88
|
+
...state,
|
|
89
|
+
persistent: nextPersistent,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Updates the current request's data.
|
|
94
|
+
case 'update-data': {
|
|
95
|
+
const { request, revision, value } = action;
|
|
96
|
+
if (request !== state.request || revision !== state.revision) return state;
|
|
97
|
+
return {
|
|
98
|
+
...state,
|
|
99
|
+
isLoading: value.isIncomplete,
|
|
100
|
+
value,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return state;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Starts fetching data and updates the resource when new data is available.
|
|
110
|
+
*/
|
|
111
|
+
function fetchData(requestInstruction, instance, abortSignal, dispatch) {
|
|
112
|
+
const { dataProvider, requestDetails, request, revision, value } = requestInstruction;
|
|
113
|
+
|
|
114
|
+
const meta = new Meta({ ...value.meta }, instance);
|
|
115
|
+
|
|
116
|
+
let promise = Promise.resolve(value);
|
|
117
|
+
|
|
118
|
+
const callback = (error, done, data) => {
|
|
119
|
+
promise = promise.then((prevValue) => {
|
|
120
|
+
try {
|
|
121
|
+
let nextValue;
|
|
122
|
+
if (error != null) {
|
|
123
|
+
nextValue = { ...prevValue, error, isIncomplete: false };
|
|
124
|
+
} else {
|
|
125
|
+
const context = {
|
|
126
|
+
name: requestDetails.name,
|
|
127
|
+
query: requestDetails.query,
|
|
128
|
+
options: requestDetails.options,
|
|
129
|
+
request,
|
|
130
|
+
revision,
|
|
131
|
+
data,
|
|
132
|
+
meta: meta.commit(prevValue.meta),
|
|
133
|
+
error: undefined,
|
|
134
|
+
isEmpty: false,
|
|
135
|
+
isIncomplete: !done,
|
|
136
|
+
isInitial: !!prevValue.isInitial && !done,
|
|
137
|
+
};
|
|
138
|
+
context.data = dataProvider.transition(context, prevValue);
|
|
139
|
+
context.data = dataProvider.recycleItems(context, prevValue);
|
|
140
|
+
nextValue = context;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
dispatch({ type: 'update-data', request, revision, value: nextValue });
|
|
144
|
+
|
|
145
|
+
return nextValue;
|
|
146
|
+
} catch {
|
|
147
|
+
return prevValue;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
dataProvider.continuousGet(
|
|
153
|
+
requestDetails.name,
|
|
154
|
+
requestDetails.query,
|
|
155
|
+
requestDetails.options,
|
|
156
|
+
meta,
|
|
157
|
+
callback,
|
|
158
|
+
abortSignal,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Provides data based on the given request details and DataProvider.
|
|
164
|
+
*
|
|
165
|
+
* Necessary configuration that is not directly specified is taken from the ConfigContext.
|
|
166
|
+
*
|
|
167
|
+
* The provided DataProvider must not be changed.
|
|
12
168
|
*/
|
|
13
169
|
function useResource({
|
|
170
|
+
/** DataProvider to be used for requests - must not be changed */
|
|
14
171
|
dataProvider: dataProviderProp,
|
|
172
|
+
/** Resource name */
|
|
15
173
|
name: nextName,
|
|
174
|
+
/** Query instructions */
|
|
16
175
|
query: nextQuery,
|
|
176
|
+
/** Disables fetching data, resulting in an empty data array */
|
|
17
177
|
empty: nextEmpty,
|
|
178
|
+
/** Query options for requests */
|
|
18
179
|
options: nextOptions,
|
|
180
|
+
/** Whether stale data should be retained during the next request - this only applies if name did not change, unless set to "very" */
|
|
19
181
|
persistent: nextPersistent,
|
|
20
182
|
...rest
|
|
21
183
|
}) {
|
|
22
184
|
const configContext = useConfigContext();
|
|
23
185
|
const currentDataProvider = dataProviderProp || configContext.dataProvider;
|
|
24
|
-
const [dataProvider] = React.useState(currentDataProvider);
|
|
25
|
-
if (dataProvider == null) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
'Unmet requirement: The DataProvider for the useResource hook is missing - Check your ConfigContext provider and the dataProvider property',
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
if (dataProvider !== currentDataProvider) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
'Constant violation: The DataProvider provided to the useResource hook must not be replaced - Check your ConfigContext provider and the dataProvider property',
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
186
|
|
|
36
|
-
const
|
|
37
|
-
React.useEffect(() => {
|
|
38
|
-
const i = dataProvider.createInstance();
|
|
39
|
-
setInstance(i ?? {});
|
|
40
|
-
return () => {
|
|
41
|
-
dataProvider.releaseInstance(i);
|
|
42
|
-
};
|
|
43
|
-
}, []);
|
|
44
|
-
|
|
45
|
-
const nextRequestDetails = React.useMemo(
|
|
187
|
+
const nextRequestDetails = useMemo(
|
|
46
188
|
() => ({ name: nextName, query: nextQuery, empty: nextEmpty, options: nextOptions }),
|
|
47
189
|
[nextName, nextQuery, nextEmpty, nextOptions],
|
|
48
190
|
);
|
|
49
|
-
const [state,
|
|
191
|
+
const [state, dispatch] = useReducer(reducer, undefined, () => {
|
|
50
192
|
const request = uniqueIdentifier();
|
|
51
193
|
const revision = uniqueIdentifier();
|
|
52
|
-
return {
|
|
194
|
+
return createRequestInstruction({
|
|
195
|
+
dataProvider: currentDataProvider,
|
|
53
196
|
requestDetails: nextRequestDetails,
|
|
54
197
|
request,
|
|
55
198
|
revision,
|
|
@@ -68,165 +211,96 @@ function useResource({
|
|
|
68
211
|
isInitial: !nextRequestDetails.empty,
|
|
69
212
|
},
|
|
70
213
|
persistent: nextPersistent,
|
|
71
|
-
};
|
|
214
|
+
});
|
|
72
215
|
});
|
|
73
|
-
const {
|
|
216
|
+
const {
|
|
217
|
+
dataProvider,
|
|
218
|
+
requestDetails,
|
|
219
|
+
request,
|
|
220
|
+
revision,
|
|
221
|
+
isLoading,
|
|
222
|
+
value,
|
|
223
|
+
persistent,
|
|
224
|
+
requestInstruction,
|
|
225
|
+
} = state;
|
|
226
|
+
|
|
227
|
+
if (dataProvider == null) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
'Unmet requirement: The DataProvider for the useResource hook is missing - Check your ConfigContext provider and the dataProvider property',
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
if (dataProvider !== currentDataProvider) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
'Constant violation: The DataProvider provided to the useResource hook must not be replaced - Check your ConfigContext provider and the dataProvider property',
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const [instance, setInstance] = useState();
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
const i = dataProvider.createInstance();
|
|
241
|
+
setInstance(i ?? {});
|
|
242
|
+
return () => {
|
|
243
|
+
dataProvider.releaseInstance(i);
|
|
244
|
+
};
|
|
245
|
+
}, [dataProvider]);
|
|
74
246
|
|
|
75
247
|
if (
|
|
76
248
|
requestDetails !== nextRequestDetails &&
|
|
77
249
|
!dataProvider.compareRequests(nextRequestDetails, requestDetails)
|
|
78
250
|
) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (prevState.persistent === 'very' && nextPersistent === 'very') {
|
|
84
|
-
isPersistent = 'very';
|
|
85
|
-
} else if (prevState.persistent && nextPersistent) {
|
|
86
|
-
isPersistent = true;
|
|
87
|
-
}
|
|
88
|
-
const shouldValuePersist =
|
|
89
|
-
!nextRequestDetails.empty &&
|
|
90
|
-
dataProvider.shouldPersist(
|
|
91
|
-
nextRequestDetails,
|
|
92
|
-
prevState.requestDetails,
|
|
93
|
-
isPersistent,
|
|
94
|
-
prevState.value,
|
|
95
|
-
);
|
|
96
|
-
return {
|
|
97
|
-
requestDetails: nextRequestDetails,
|
|
98
|
-
request: nextRequest,
|
|
99
|
-
revision: nextRevision,
|
|
100
|
-
isLoading: !nextRequestDetails.empty,
|
|
101
|
-
value: shouldValuePersist
|
|
102
|
-
? prevState.value
|
|
103
|
-
: {
|
|
104
|
-
name: nextRequestDetails.name,
|
|
105
|
-
query: nextRequestDetails.query,
|
|
106
|
-
options: nextRequestDetails.options,
|
|
107
|
-
request: nextRequest,
|
|
108
|
-
revision: nextRevision,
|
|
109
|
-
data: [],
|
|
110
|
-
meta: {},
|
|
111
|
-
error: undefined,
|
|
112
|
-
isEmpty: !!nextRequestDetails.empty,
|
|
113
|
-
isIncomplete: !nextRequestDetails.empty,
|
|
114
|
-
isInitial: !nextRequestDetails.empty,
|
|
115
|
-
},
|
|
116
|
-
persistent: nextPersistent,
|
|
117
|
-
};
|
|
251
|
+
dispatch({
|
|
252
|
+
type: 'next-request',
|
|
253
|
+
requestDetails: nextRequestDetails,
|
|
254
|
+
persistent: nextPersistent,
|
|
118
255
|
});
|
|
119
256
|
} else if (persistent !== nextPersistent) {
|
|
120
|
-
|
|
257
|
+
dispatch({ type: 'set-persistence', persistent: nextPersistent });
|
|
121
258
|
}
|
|
122
259
|
|
|
123
|
-
const notify =
|
|
260
|
+
const notify = useCallback(
|
|
124
261
|
async () =>
|
|
125
262
|
new Promise((resolve) => {
|
|
126
|
-
|
|
127
|
-
const nextRevision = uniqueIdentifier(currentState.revision);
|
|
128
|
-
resolve({ request: currentState.request, revision: nextRevision });
|
|
129
|
-
return { ...currentState, isLoading: true, revision: nextRevision };
|
|
130
|
-
});
|
|
263
|
+
dispatch({ type: 'next-revision', notify: resolve });
|
|
131
264
|
}),
|
|
132
265
|
[],
|
|
133
266
|
);
|
|
134
267
|
|
|
135
268
|
// DataProvider events
|
|
136
|
-
|
|
269
|
+
useEffect(() => {
|
|
137
270
|
if (requestDetails.empty) return undefined;
|
|
138
271
|
|
|
139
272
|
const unsubscribe = dataProvider.subscribe(requestDetails.name, notify);
|
|
140
273
|
return unsubscribe;
|
|
141
274
|
}, [requestDetails.empty, dataProvider, requestDetails.name, notify]);
|
|
142
275
|
|
|
143
|
-
|
|
144
|
-
|
|
276
|
+
// Fetch data when instructed
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
if (instance == null || requestInstruction.requestDetails.empty) return undefined;
|
|
145
279
|
|
|
146
280
|
const abortSignal = new AbortSignal();
|
|
147
281
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
let promise = Promise.resolve(state);
|
|
151
|
-
|
|
152
|
-
const callback = (error, done, data) => {
|
|
153
|
-
promise = promise.then((prevState) => {
|
|
154
|
-
try {
|
|
155
|
-
let nextState;
|
|
156
|
-
|
|
157
|
-
if (error != null) {
|
|
158
|
-
nextState = {
|
|
159
|
-
...prevState,
|
|
160
|
-
isLoading: false,
|
|
161
|
-
value: {
|
|
162
|
-
...prevState.value,
|
|
163
|
-
error,
|
|
164
|
-
isIncomplete: false,
|
|
165
|
-
},
|
|
166
|
-
};
|
|
167
|
-
} else {
|
|
168
|
-
const context = {
|
|
169
|
-
name: requestDetails.name,
|
|
170
|
-
query: requestDetails.query,
|
|
171
|
-
options: requestDetails.options,
|
|
172
|
-
request,
|
|
173
|
-
revision,
|
|
174
|
-
data,
|
|
175
|
-
meta: meta.commit(prevState.value.meta),
|
|
176
|
-
error: undefined,
|
|
177
|
-
isEmpty: false,
|
|
178
|
-
isIncomplete: !done,
|
|
179
|
-
isInitial: !!prevState.isInitial && !done,
|
|
180
|
-
};
|
|
181
|
-
context.data = dataProvider.transition(context, prevState.value);
|
|
182
|
-
context.data = dataProvider.recycleItems(context, prevState.value);
|
|
183
|
-
|
|
184
|
-
nextState = {
|
|
185
|
-
...prevState,
|
|
186
|
-
isLoading: !done,
|
|
187
|
-
value: context,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
setState((otherState) => {
|
|
192
|
-
if (request !== otherState.request || revision !== otherState.revision) {
|
|
193
|
-
return otherState;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return nextState;
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
return nextState;
|
|
200
|
-
} catch {
|
|
201
|
-
return prevState;
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
dataProvider.continuousGet(
|
|
207
|
-
requestDetails.name,
|
|
208
|
-
requestDetails.query,
|
|
209
|
-
requestDetails.options,
|
|
210
|
-
meta,
|
|
211
|
-
callback,
|
|
212
|
-
abortSignal,
|
|
213
|
-
);
|
|
282
|
+
// Start fetching data.
|
|
283
|
+
fetchData(requestInstruction, instance, abortSignal, dispatch);
|
|
214
284
|
|
|
215
285
|
return () => {
|
|
286
|
+
// Abort fetching data when another request is pending or the React component is unmounted.
|
|
216
287
|
abortSignal.abort();
|
|
217
288
|
};
|
|
218
|
-
}, [instance,
|
|
289
|
+
}, [instance, requestInstruction]);
|
|
219
290
|
|
|
220
291
|
const isStale = revision !== value.revision;
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
292
|
+
const nextRequest = isStale ? request : value.request;
|
|
293
|
+
const nextRevision = isStale ? revision : value.revision;
|
|
294
|
+
const next = useMemo(
|
|
295
|
+
() => ({ request: nextRequest, revision: nextRevision }),
|
|
296
|
+
[nextRequest, nextRevision],
|
|
224
297
|
);
|
|
225
|
-
const context =
|
|
298
|
+
const context = useMemo(
|
|
226
299
|
() => ({ ...value, dataProvider, isLoading, isStale, next, notify }),
|
|
227
300
|
[value, dataProvider, isLoading, isStale, next, notify],
|
|
228
301
|
);
|
|
229
302
|
|
|
303
|
+
// Apply context plugins and return the final context.
|
|
230
304
|
return dataProvider.contextPlugins.reduce((result, fn) => fn(result, rest), context);
|
|
231
305
|
}
|
|
232
306
|
|