@civet/core 1.2.6 → 1.3.0
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 +5 -6
- package/lib/DataProvider.js +7 -11
- package/lib/Meta.js +6 -7
- package/lib/Notifier.js +4 -5
- package/lib/Resource.js +18 -15
- 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 +223 -149
- 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} +5 -7
- package/src/context.js +5 -5
- package/src/createPlugin.js +0 -1
- package/src/index.js +3 -3
- package/src/useResource.js +214 -148
|
@@ -1,8 +1,6 @@
|
|
|
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 = {
|
|
@@ -27,12 +25,12 @@ const propTypes = {
|
|
|
27
25
|
* The provided DataProvider must not be replaced.
|
|
28
26
|
*/
|
|
29
27
|
function Resource({ dataProvider, name, query, empty, options, persistent, children, ...rest }) {
|
|
30
|
-
const context = useResource({ dataProvider, name, query, empty, options, persistent });
|
|
28
|
+
const context = useResource({ dataProvider, name, query, empty, options, persistent, ...rest });
|
|
31
29
|
|
|
32
30
|
return context.dataProvider.uiPlugins.reduceRight(
|
|
33
|
-
(next, Plugin) =>
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
(next, Plugin) =>
|
|
32
|
+
// eslint-disable-next-line react/display-name
|
|
33
|
+
(result) => (
|
|
36
34
|
<Plugin {...rest} context={result}>
|
|
37
35
|
{next}
|
|
38
36
|
</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,12 +1,166 @@
|
|
|
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
|
-
*
|
|
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
|
+
* Makes data from a DataProvider available.
|
|
10
164
|
* If not explicitly specified, necessary configuration is taken from the nearest <ConfigProvider>.
|
|
11
165
|
* The provided DataProvider must not be replaced.
|
|
12
166
|
*/
|
|
@@ -21,35 +175,16 @@ function useResource({
|
|
|
21
175
|
}) {
|
|
22
176
|
const configContext = useConfigContext();
|
|
23
177
|
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
178
|
|
|
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(
|
|
179
|
+
const nextRequestDetails = useMemo(
|
|
46
180
|
() => ({ name: nextName, query: nextQuery, empty: nextEmpty, options: nextOptions }),
|
|
47
181
|
[nextName, nextQuery, nextEmpty, nextOptions],
|
|
48
182
|
);
|
|
49
|
-
const [state,
|
|
183
|
+
const [state, dispatch] = useReducer(reducer, undefined, () => {
|
|
50
184
|
const request = uniqueIdentifier();
|
|
51
185
|
const revision = uniqueIdentifier();
|
|
52
|
-
return {
|
|
186
|
+
return createRequestInstruction({
|
|
187
|
+
dataProvider: currentDataProvider,
|
|
53
188
|
requestDetails: nextRequestDetails,
|
|
54
189
|
request,
|
|
55
190
|
revision,
|
|
@@ -68,165 +203,96 @@ function useResource({
|
|
|
68
203
|
isInitial: !nextRequestDetails.empty,
|
|
69
204
|
},
|
|
70
205
|
persistent: nextPersistent,
|
|
71
|
-
};
|
|
206
|
+
});
|
|
72
207
|
});
|
|
73
|
-
const {
|
|
208
|
+
const {
|
|
209
|
+
dataProvider,
|
|
210
|
+
requestDetails,
|
|
211
|
+
request,
|
|
212
|
+
revision,
|
|
213
|
+
isLoading,
|
|
214
|
+
value,
|
|
215
|
+
persistent,
|
|
216
|
+
requestInstruction,
|
|
217
|
+
} = state;
|
|
218
|
+
|
|
219
|
+
if (dataProvider == null) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
'Unmet requirement: The DataProvider for the useResource hook is missing - Check your ConfigContext provider and the dataProvider property',
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (dataProvider !== currentDataProvider) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
'Constant violation: The DataProvider provided to the useResource hook must not be replaced - Check your ConfigContext provider and the dataProvider property',
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const [instance, setInstance] = useState();
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
const i = dataProvider.createInstance();
|
|
233
|
+
setInstance(i ?? {});
|
|
234
|
+
return () => {
|
|
235
|
+
dataProvider.releaseInstance(i);
|
|
236
|
+
};
|
|
237
|
+
}, [dataProvider]);
|
|
74
238
|
|
|
75
239
|
if (
|
|
76
240
|
requestDetails !== nextRequestDetails &&
|
|
77
241
|
!dataProvider.compareRequests(nextRequestDetails, requestDetails)
|
|
78
242
|
) {
|
|
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
|
-
};
|
|
243
|
+
dispatch({
|
|
244
|
+
type: 'next-request',
|
|
245
|
+
requestDetails: nextRequestDetails,
|
|
246
|
+
persistent: nextPersistent,
|
|
118
247
|
});
|
|
119
248
|
} else if (persistent !== nextPersistent) {
|
|
120
|
-
|
|
249
|
+
dispatch({ type: 'set-persistence', persistent: nextPersistent });
|
|
121
250
|
}
|
|
122
251
|
|
|
123
|
-
const notify =
|
|
252
|
+
const notify = useCallback(
|
|
124
253
|
async () =>
|
|
125
254
|
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
|
-
});
|
|
255
|
+
dispatch({ type: 'next-revision', notify: resolve });
|
|
131
256
|
}),
|
|
132
257
|
[],
|
|
133
258
|
);
|
|
134
259
|
|
|
135
260
|
// DataProvider events
|
|
136
|
-
|
|
261
|
+
useEffect(() => {
|
|
137
262
|
if (requestDetails.empty) return undefined;
|
|
138
263
|
|
|
139
264
|
const unsubscribe = dataProvider.subscribe(requestDetails.name, notify);
|
|
140
265
|
return unsubscribe;
|
|
141
266
|
}, [requestDetails.empty, dataProvider, requestDetails.name, notify]);
|
|
142
267
|
|
|
143
|
-
|
|
144
|
-
|
|
268
|
+
// Fetch data when instructed
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
if (instance == null || requestInstruction.requestDetails.empty) return undefined;
|
|
145
271
|
|
|
146
272
|
const abortSignal = new AbortSignal();
|
|
147
273
|
|
|
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
|
-
);
|
|
274
|
+
// Start fetching data.
|
|
275
|
+
fetchData(requestInstruction, instance, abortSignal, dispatch);
|
|
214
276
|
|
|
215
277
|
return () => {
|
|
278
|
+
// Abort fetching data when another request is pending or the React component is unmounted.
|
|
216
279
|
abortSignal.abort();
|
|
217
280
|
};
|
|
218
|
-
}, [instance,
|
|
281
|
+
}, [instance, requestInstruction]);
|
|
219
282
|
|
|
220
283
|
const isStale = revision !== value.revision;
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
284
|
+
const nextRequest = isStale ? request : value.request;
|
|
285
|
+
const nextRevision = isStale ? revision : value.revision;
|
|
286
|
+
const next = useMemo(
|
|
287
|
+
() => ({ request: nextRequest, revision: nextRevision }),
|
|
288
|
+
[nextRequest, nextRevision],
|
|
224
289
|
);
|
|
225
|
-
const context =
|
|
290
|
+
const context = useMemo(
|
|
226
291
|
() => ({ ...value, dataProvider, isLoading, isStale, next, notify }),
|
|
227
292
|
[value, dataProvider, isLoading, isStale, next, notify],
|
|
228
293
|
);
|
|
229
294
|
|
|
295
|
+
// Apply context plugins and return the final context.
|
|
230
296
|
return dataProvider.contextPlugins.reduce((result, fn) => fn(result, rest), context);
|
|
231
297
|
}
|
|
232
298
|
|