@khanacademy/wonder-blocks-data 2.3.2 → 3.0.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/CHANGELOG.md +14 -0
- package/dist/es/index.js +210 -439
- package/dist/index.js +235 -478
- package/docs.md +19 -13
- package/package.json +6 -7
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +40 -160
- package/src/__tests__/generated-snapshot.test.js +15 -195
- package/src/components/__tests__/data.test.js +159 -965
- package/src/components/__tests__/intercept-data.test.js +9 -66
- package/src/components/__tests__/track-data.test.js +6 -5
- package/src/components/data.js +9 -119
- package/src/components/data.md +38 -60
- package/src/components/intercept-context.js +2 -3
- package/src/components/intercept-data.js +2 -34
- package/src/components/intercept-data.md +7 -105
- package/src/hooks/__tests__/use-data.test.js +826 -0
- package/src/hooks/use-data.js +143 -0
- package/src/index.js +1 -3
- package/src/util/__tests__/memory-cache.test.js +134 -35
- package/src/util/__tests__/request-fulfillment.test.js +21 -36
- package/src/util/__tests__/request-handler.test.js +30 -30
- package/src/util/__tests__/request-tracking.test.js +29 -30
- package/src/util/__tests__/response-cache.test.js +521 -561
- package/src/util/__tests__/result-from-cache-entry.test.js +68 -0
- package/src/util/memory-cache.js +20 -15
- package/src/util/request-fulfillment.js +4 -0
- package/src/util/request-handler.js +4 -28
- package/src/util/request-handler.md +0 -32
- package/src/util/request-tracking.js +2 -3
- package/src/util/response-cache.js +50 -110
- package/src/util/result-from-cache-entry.js +38 -0
- package/src/util/types.js +14 -35
- package/LICENSE +0 -21
- package/src/components/__tests__/intercept-cache.test.js +0 -124
- package/src/components/__tests__/internal-data.test.js +0 -1030
- package/src/components/intercept-cache.js +0 -79
- package/src/components/intercept-cache.md +0 -103
- package/src/components/internal-data.js +0 -219
- package/src/util/__tests__/no-cache.test.js +0 -112
- package/src/util/no-cache.js +0 -66
- package/src/util/no-cache.md +0 -66
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import InterceptContext from "./intercept-context.js";
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
ValidData,
|
|
8
|
-
IRequestHandler,
|
|
9
|
-
InterceptCacheFn,
|
|
10
|
-
} from "../util/types.js";
|
|
11
|
-
|
|
12
|
-
type Props<TOptions, TData> = {|
|
|
13
|
-
/**
|
|
14
|
-
* A handler of the type to be intercepted.
|
|
15
|
-
*/
|
|
16
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* The children to render within this component. Any cache lookups by `Data`
|
|
20
|
-
* components that use a handler of the same type as the handler for this
|
|
21
|
-
* component that are rendered within these children will be intercepted by
|
|
22
|
-
* this component (unless another `InterceptData` component overrides this
|
|
23
|
-
* one).
|
|
24
|
-
*/
|
|
25
|
-
children: React.Node,
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Called to retrieve a cache entry.
|
|
29
|
-
*
|
|
30
|
-
* The method takes a key, the options for a request, and the existing
|
|
31
|
-
* cached entry if one exists.
|
|
32
|
-
*
|
|
33
|
-
* If this returns null, the default behavior occurs.
|
|
34
|
-
*/
|
|
35
|
-
getEntry: InterceptCacheFn<TOptions, TData>,
|
|
36
|
-
|};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* This component provides a mechanism to intercept cache lookups for the
|
|
40
|
-
* type of a given handler and provide alternative values. This is mostly
|
|
41
|
-
* useful for testing.
|
|
42
|
-
*
|
|
43
|
-
* This does not modify the cache in any way. If you want to intercept
|
|
44
|
-
* requests and cache based on the intercept, then use `InterceptData`.
|
|
45
|
-
*
|
|
46
|
-
* This component is generally not suitable for use in production code as it
|
|
47
|
-
* can prevent predictable functioning of the Wonder Blocks Data framework.
|
|
48
|
-
*
|
|
49
|
-
* These components do not chain. If a different `InterceptCache` instance is
|
|
50
|
-
* rendered within this one that intercepts the same handler type, then that
|
|
51
|
-
* new instance will replace this interceptor for its children.
|
|
52
|
-
*/
|
|
53
|
-
export default class InterceptCache<
|
|
54
|
-
TOptions,
|
|
55
|
-
TData: ValidData,
|
|
56
|
-
> extends React.Component<Props<TOptions, TData>> {
|
|
57
|
-
render(): React.Node {
|
|
58
|
-
return (
|
|
59
|
-
<InterceptContext.Consumer>
|
|
60
|
-
{(value) => {
|
|
61
|
-
const handlerType = this.props.handler.type;
|
|
62
|
-
const interceptor = {
|
|
63
|
-
...value[handlerType],
|
|
64
|
-
getEntry: this.props.getEntry,
|
|
65
|
-
};
|
|
66
|
-
const newValue = {
|
|
67
|
-
...value,
|
|
68
|
-
[handlerType]: interceptor,
|
|
69
|
-
};
|
|
70
|
-
return (
|
|
71
|
-
<InterceptContext.Provider value={newValue}>
|
|
72
|
-
{this.props.children}
|
|
73
|
-
</InterceptContext.Provider>
|
|
74
|
-
);
|
|
75
|
-
}}
|
|
76
|
-
</InterceptContext.Consumer>
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
Although it is possible to use the `initializeCache` method to setup test cases
|
|
2
|
-
when working with the `Data` component, that can be a little cumbersome since
|
|
3
|
-
it can only be called once and it requires knowledge of the cache structure.
|
|
4
|
-
|
|
5
|
-
Instead of jumping through those hoops, you can use the `InterceptCache`
|
|
6
|
-
component and provide an override method that will get called when looking up
|
|
7
|
-
cached values. This component takes three props:
|
|
8
|
-
|
|
9
|
-
- children to be rendered
|
|
10
|
-
- the handler for data caches that are to be intercepted
|
|
11
|
-
- a function for processing the intercept
|
|
12
|
-
|
|
13
|
-
The intercept function has the form:
|
|
14
|
-
|
|
15
|
-
```js static
|
|
16
|
-
(options: TOptions, cacheEntry: ?$ReadOnly<CacheEntry<TData>>) => ?$ReadOnly<CacheEntry<TData>>
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
`options` are the options that would configure a data request that leads to the
|
|
20
|
-
cached result.
|
|
21
|
-
`cacheEntry` is the existing entry in the data cache.
|
|
22
|
-
|
|
23
|
-
If the method returns `null`, the default behavior occurs. This means that if
|
|
24
|
-
`cacheEntry` has a value, that will be used; otherwise, a request will be
|
|
25
|
-
made for data via the relevant handler assigned to the `Data` component being
|
|
26
|
-
intercepted.
|
|
27
|
-
|
|
28
|
-
```jsx
|
|
29
|
-
import {Body, BodyMonospace} from "@khanacademy/wonder-blocks-typography";
|
|
30
|
-
import {View} from "@khanacademy/wonder-blocks-core";
|
|
31
|
-
import {InterceptCache, Data, RequestHandler} from "@khanacademy/wonder-blocks-data";
|
|
32
|
-
import {Strut} from "@khanacademy/wonder-blocks-layout";
|
|
33
|
-
import Color from "@khanacademy/wonder-blocks-color";
|
|
34
|
-
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
35
|
-
|
|
36
|
-
class MyHandler extends RequestHandler {
|
|
37
|
-
constructor() {
|
|
38
|
-
super("INTERCEPT_CACHE_HANDLER");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* fulfillRequest should not get called as we already have data cached.
|
|
43
|
-
*/
|
|
44
|
-
fulfillRequest(options) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
"If you're seeing this error, the examples are broken.",
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
shouldRefreshCache(options, cachedEntry) {
|
|
51
|
-
/**
|
|
52
|
-
* For our purposes, the cache never needs a refresh.
|
|
53
|
-
*/
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const handler = new MyHandler();
|
|
59
|
-
const getEntryIntercept = function(options) {
|
|
60
|
-
if (options === "ERROR") {
|
|
61
|
-
return {error: "I'm an ERROR from the cache interceptor"};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return {data: "I'm DATA from the cache interceptor"};
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
<InterceptCache handler={handler} getEntry={getEntryIntercept}>
|
|
68
|
-
<View>
|
|
69
|
-
<Body>This intercepted cache has data!</Body>
|
|
70
|
-
<Data handler={handler} options={"DATA"}>
|
|
71
|
-
{({loading, data}) => {
|
|
72
|
-
if (loading) {
|
|
73
|
-
return "If you see this, the example is broken!";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<BodyMonospace>{data}</BodyMonospace>
|
|
78
|
-
);
|
|
79
|
-
}}
|
|
80
|
-
</Data>
|
|
81
|
-
</View>
|
|
82
|
-
<Strut size={Spacing.small_12} />
|
|
83
|
-
<View>
|
|
84
|
-
<Body>This intercepted cache has error!</Body>
|
|
85
|
-
<Data handler={handler} options={"ERROR"}>
|
|
86
|
-
{({loading, error}) => {
|
|
87
|
-
if (loading) {
|
|
88
|
-
return "If you see this, the example is broken!";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<BodyMonospace style={{color: Color.red}}>ERROR: {error}</BodyMonospace>
|
|
93
|
-
);
|
|
94
|
-
}}
|
|
95
|
-
</Data>
|
|
96
|
-
</View>
|
|
97
|
-
</InterceptCache>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
This component can also be useful if you want to avoid a data request because
|
|
101
|
-
a parent component already has the data you need. Using this component, you
|
|
102
|
-
can map already cached data of one request (or a portion thereof) to another
|
|
103
|
-
without affecting the cache itself.
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import {Server} from "@khanacademy/wonder-blocks-core";
|
|
4
|
-
|
|
5
|
-
import {RequestFulfillment} from "../util/request-fulfillment.js";
|
|
6
|
-
import {TrackerContext} from "../util/request-tracking.js";
|
|
7
|
-
|
|
8
|
-
import type {
|
|
9
|
-
ValidData,
|
|
10
|
-
CacheEntry,
|
|
11
|
-
Result,
|
|
12
|
-
IRequestHandler,
|
|
13
|
-
} from "../util/types.js";
|
|
14
|
-
|
|
15
|
-
type Props<TOptions, TData> = {|
|
|
16
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
17
|
-
options: TOptions,
|
|
18
|
-
getEntry: (
|
|
19
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
20
|
-
options: TOptions,
|
|
21
|
-
) => ?$ReadOnly<CacheEntry<TData>>,
|
|
22
|
-
children: (result: Result<TData>) => React.Node,
|
|
23
|
-
|};
|
|
24
|
-
|
|
25
|
-
type State<TData> = {|
|
|
26
|
-
loading: boolean,
|
|
27
|
-
data: ?TData,
|
|
28
|
-
error: ?string,
|
|
29
|
-
|};
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* This component is responsible for actually handling the data request.
|
|
33
|
-
* It is wrapped by Data in order to support intercepts and be exported for use.
|
|
34
|
-
*
|
|
35
|
-
* INTERNAL USE ONLY
|
|
36
|
-
*/
|
|
37
|
-
export default class InternalData<
|
|
38
|
-
TOptions,
|
|
39
|
-
TData: ValidData,
|
|
40
|
-
> extends React.Component<Props<TOptions, TData>, State<TData>> {
|
|
41
|
-
_mounted: boolean;
|
|
42
|
-
|
|
43
|
-
constructor(props: Props<TOptions, TData>) {
|
|
44
|
-
super(props);
|
|
45
|
-
|
|
46
|
-
this.state = this._buildStateAndfulfillNeeds(props);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
componentDidMount() {
|
|
50
|
-
this._mounted = true;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
shouldComponentUpdate(
|
|
54
|
-
nextProps: $ReadOnly<Props<TOptions, TData>>,
|
|
55
|
-
nextState: $ReadOnly<State<TData>>,
|
|
56
|
-
): boolean {
|
|
57
|
-
/**
|
|
58
|
-
* We only bother updating if our state changed.
|
|
59
|
-
*
|
|
60
|
-
* And we only update the state if props changed
|
|
61
|
-
* or we got new data/error.
|
|
62
|
-
*/
|
|
63
|
-
if (!this._propsMatch(nextProps)) {
|
|
64
|
-
const newState = this._buildStateAndfulfillNeeds(nextProps);
|
|
65
|
-
this.setState(newState);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
this.state.loading !== nextState.loading ||
|
|
70
|
-
this.state.data !== nextState.data ||
|
|
71
|
-
this.state.error !== nextState.error
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
componentWillUnmount() {
|
|
76
|
-
this._mounted = false;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
_propsMatch(otherProps: $Shape<Props<TOptions, TData>>): boolean {
|
|
80
|
-
const {handler, options} = this.props;
|
|
81
|
-
const {handler: prevHandler, options: prevOptions} = otherProps;
|
|
82
|
-
return (
|
|
83
|
-
handler === prevHandler &&
|
|
84
|
-
handler.getKey(options) === prevHandler.getKey(prevOptions)
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
_buildStateAndfulfillNeeds(
|
|
89
|
-
propsAtFulfillment: $ReadOnly<Props<TOptions, TData>>,
|
|
90
|
-
): State<TData> {
|
|
91
|
-
const {getEntry, handler, options} = propsAtFulfillment;
|
|
92
|
-
const cachedData = getEntry(handler, options);
|
|
93
|
-
if (
|
|
94
|
-
!Server.isServerSide() &&
|
|
95
|
-
(cachedData == null ||
|
|
96
|
-
handler.shouldRefreshCache(options, cachedData))
|
|
97
|
-
) {
|
|
98
|
-
/**
|
|
99
|
-
* We're not on the server, the cache missed, or our handler says
|
|
100
|
-
* we should refresh the cache.
|
|
101
|
-
*
|
|
102
|
-
* Therefore, we need to request data.
|
|
103
|
-
*
|
|
104
|
-
* We have to do this here from the constructor so that this
|
|
105
|
-
* data request is tracked when performing server-side rendering.
|
|
106
|
-
*/
|
|
107
|
-
RequestFulfillment.Default.fulfill(handler, options)
|
|
108
|
-
.then((cacheEntry) => {
|
|
109
|
-
/**
|
|
110
|
-
* We get here, we should have updated the cache.
|
|
111
|
-
* However, we need to update the component, but we
|
|
112
|
-
* should only do that if the props are the same as they
|
|
113
|
-
* were when this was called.
|
|
114
|
-
*/
|
|
115
|
-
if (this._mounted && this._propsMatch(propsAtFulfillment)) {
|
|
116
|
-
this.setState({
|
|
117
|
-
loading: false,
|
|
118
|
-
data: cacheEntry.data,
|
|
119
|
-
error: cacheEntry.error,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
return null;
|
|
123
|
-
})
|
|
124
|
-
.catch((e) => {
|
|
125
|
-
/**
|
|
126
|
-
* We should never get here, but if we do.
|
|
127
|
-
*/
|
|
128
|
-
// eslint-disable-next-line no-console
|
|
129
|
-
console.error(
|
|
130
|
-
`Unexpected error occurred during data fulfillment: ${e}`,
|
|
131
|
-
);
|
|
132
|
-
if (this._mounted && this._propsMatch(propsAtFulfillment)) {
|
|
133
|
-
this.setState({
|
|
134
|
-
loading: false,
|
|
135
|
-
data: null,
|
|
136
|
-
error: typeof e === "string" ? e : e.message,
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
return null;
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* This is the default response for the server and for the initial
|
|
145
|
-
* client-side render if we have cachedData.
|
|
146
|
-
*
|
|
147
|
-
* This ensures we don't make promises we don't want when doing
|
|
148
|
-
* server-side rendering. Instead, we either have data from the cache
|
|
149
|
-
* or we don't.
|
|
150
|
-
*/
|
|
151
|
-
return {
|
|
152
|
-
loading: cachedData == null,
|
|
153
|
-
data: cachedData && cachedData.data,
|
|
154
|
-
error: cachedData && cachedData.error,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
_resultFromState(): Result<TData> {
|
|
159
|
-
const {loading, data, error} = this.state;
|
|
160
|
-
|
|
161
|
-
if (loading) {
|
|
162
|
-
return {
|
|
163
|
-
loading: true,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (data != null) {
|
|
168
|
-
return {
|
|
169
|
-
loading: false,
|
|
170
|
-
data,
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (error == null) {
|
|
175
|
-
// We should never get here ever.
|
|
176
|
-
throw new Error(
|
|
177
|
-
"Loaded result has invalid state where data and error are missing",
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
loading: false,
|
|
183
|
-
error,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
_renderContent(result: Result<TData>): React.Node {
|
|
188
|
-
const {children} = this.props;
|
|
189
|
-
return children(result);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
_renderWithTrackingContext(result: Result<TData>): React.Node {
|
|
193
|
-
return (
|
|
194
|
-
<TrackerContext.Consumer>
|
|
195
|
-
{(track) => {
|
|
196
|
-
/**
|
|
197
|
-
* If data tracking wasn't enabled, don't do it.
|
|
198
|
-
*/
|
|
199
|
-
if (track != null) {
|
|
200
|
-
track(this.props.handler, this.props.options);
|
|
201
|
-
}
|
|
202
|
-
return this._renderContent(result);
|
|
203
|
-
}}
|
|
204
|
-
</TrackerContext.Consumer>
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
render(): React.Node {
|
|
209
|
-
const result = this._resultFromState();
|
|
210
|
-
// We only track data requests when we are server-side and we don't
|
|
211
|
-
// already have a result. The existence of a result is indicated by the
|
|
212
|
-
// loading flag being false.
|
|
213
|
-
if (result.loading && Server.isServerSide()) {
|
|
214
|
-
return this._renderWithTrackingContext(result);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return this._renderContent(result);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import NoCache from "../no-cache.js";
|
|
3
|
-
|
|
4
|
-
import type {IRequestHandler} from "../types.js";
|
|
5
|
-
|
|
6
|
-
describe("NoCache", () => {
|
|
7
|
-
describe("#store", () => {
|
|
8
|
-
it("should not throw", () => {
|
|
9
|
-
// Arrange
|
|
10
|
-
const cache = new NoCache();
|
|
11
|
-
const fakeHandler: IRequestHandler<string, string> = {
|
|
12
|
-
getKey: () => "MY_KEY",
|
|
13
|
-
type: "MY_HANDLER",
|
|
14
|
-
shouldRefreshCache: () => false,
|
|
15
|
-
fulfillRequest: jest.fn(),
|
|
16
|
-
cache: null,
|
|
17
|
-
hydrate: true,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// Act
|
|
21
|
-
const underTest = () =>
|
|
22
|
-
cache.store<string, string>(fakeHandler, "options", {
|
|
23
|
-
data: "data",
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// Assert
|
|
27
|
-
expect(underTest).not.toThrow();
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe("#retrieve", () => {
|
|
32
|
-
it("should return null", () => {
|
|
33
|
-
// Arrange
|
|
34
|
-
const cache = new NoCache();
|
|
35
|
-
const fakeHandler: IRequestHandler<string, string> = {
|
|
36
|
-
getKey: () => "MY_KEY",
|
|
37
|
-
type: "MY_HANDLER",
|
|
38
|
-
shouldRefreshCache: () => false,
|
|
39
|
-
fulfillRequest: jest.fn(),
|
|
40
|
-
cache: null,
|
|
41
|
-
hydrate: true,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Act
|
|
45
|
-
const result = cache.retrieve(fakeHandler, "options");
|
|
46
|
-
|
|
47
|
-
// Assert
|
|
48
|
-
expect(result).toBeNull();
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
describe("#remove", () => {
|
|
53
|
-
it("should return false", () => {
|
|
54
|
-
// Arrange
|
|
55
|
-
const cache = new NoCache();
|
|
56
|
-
const fakeHandler: IRequestHandler<string, string> = {
|
|
57
|
-
getKey: () => "MY_KEY",
|
|
58
|
-
type: "MY_HANDLER",
|
|
59
|
-
shouldRefreshCache: () => false,
|
|
60
|
-
fulfillRequest: jest.fn(),
|
|
61
|
-
cache: null,
|
|
62
|
-
hydrate: true,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// Act
|
|
66
|
-
const result = cache.remove(fakeHandler, "options");
|
|
67
|
-
|
|
68
|
-
// Assert
|
|
69
|
-
expect(result).toBeFalsy();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe("#removeAll", () => {
|
|
74
|
-
it("should return 0 without predicate", () => {
|
|
75
|
-
// Arrange
|
|
76
|
-
const cache = new NoCache();
|
|
77
|
-
const fakeHandler: IRequestHandler<string, string> = {
|
|
78
|
-
getKey: () => "MY_KEY",
|
|
79
|
-
type: "MY_HANDLER",
|
|
80
|
-
shouldRefreshCache: () => false,
|
|
81
|
-
fulfillRequest: jest.fn(),
|
|
82
|
-
cache: null,
|
|
83
|
-
hydrate: true,
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Act
|
|
87
|
-
const result = cache.removeAll(fakeHandler);
|
|
88
|
-
|
|
89
|
-
// Assert
|
|
90
|
-
expect(result).toBe(0);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("should return 0 with predicate", () => {
|
|
94
|
-
// Arrange
|
|
95
|
-
const cache = new NoCache();
|
|
96
|
-
const fakeHandler: IRequestHandler<string, string> = {
|
|
97
|
-
getKey: () => "MY_KEY",
|
|
98
|
-
type: "MY_HANDLER",
|
|
99
|
-
shouldRefreshCache: () => false,
|
|
100
|
-
fulfillRequest: jest.fn(),
|
|
101
|
-
cache: null,
|
|
102
|
-
hydrate: true,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// Act
|
|
106
|
-
const result = cache.removeAll(fakeHandler, () => true);
|
|
107
|
-
|
|
108
|
-
// Assert
|
|
109
|
-
expect(result).toBe(0);
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
});
|
package/src/util/no-cache.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import type {ValidData, ICache, CacheEntry, IRequestHandler} from "./types.js";
|
|
3
|
-
|
|
4
|
-
let defaultInstance: ?ICache<any, any> = null;
|
|
5
|
-
/**
|
|
6
|
-
* This is a cache implementation to use when no caching is wanted.
|
|
7
|
-
*
|
|
8
|
-
* Use this with your request handler if you want to support server-side
|
|
9
|
-
* rendering of your data requests, but want to ensure data is never cached
|
|
10
|
-
* on the client-side.
|
|
11
|
-
*
|
|
12
|
-
* This is better than having `shouldRefreshCache` always return `true` in the
|
|
13
|
-
* handler as this ensures that cache space and memory are never used for the
|
|
14
|
-
* requested data after hydration has finished.
|
|
15
|
-
*/
|
|
16
|
-
export default class NoCache<TOptions, TData: ValidData>
|
|
17
|
-
implements ICache<TOptions, TData> {
|
|
18
|
-
static get Default(): ICache<TOptions, TData> {
|
|
19
|
-
if (defaultInstance == null) {
|
|
20
|
-
defaultInstance = new NoCache<TOptions, TData>();
|
|
21
|
-
}
|
|
22
|
-
return defaultInstance;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
store: <TOptions, TData: ValidData>(
|
|
26
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
27
|
-
options: TOptions,
|
|
28
|
-
entry: CacheEntry<TData>,
|
|
29
|
-
) => void = <TOptions, TData: ValidData>(
|
|
30
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
31
|
-
options: TOptions,
|
|
32
|
-
entry: CacheEntry<TData>,
|
|
33
|
-
): void => {
|
|
34
|
-
/* empty */
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
retrieve: <TOptions, TData: ValidData>(
|
|
38
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
39
|
-
options: TOptions,
|
|
40
|
-
) => ?CacheEntry<TData> = <TOptions, TData: ValidData>(
|
|
41
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
42
|
-
options: TOptions,
|
|
43
|
-
): ?CacheEntry<TData> => null;
|
|
44
|
-
|
|
45
|
-
remove: <TOptions, TData: ValidData>(
|
|
46
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
47
|
-
options: TOptions,
|
|
48
|
-
) => boolean = <TOptions, TData: ValidData>(
|
|
49
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
50
|
-
options: TOptions,
|
|
51
|
-
): boolean => false;
|
|
52
|
-
|
|
53
|
-
removeAll: <TOptions, TData: ValidData>(
|
|
54
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
55
|
-
predicate?: (
|
|
56
|
-
key: string,
|
|
57
|
-
cachedEntry: $ReadOnly<CacheEntry<TData>>,
|
|
58
|
-
) => boolean,
|
|
59
|
-
) => number = <TOptions, TData: ValidData>(
|
|
60
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
61
|
-
predicate?: (
|
|
62
|
-
key: string,
|
|
63
|
-
cachedEntry: $ReadOnly<CacheEntry<TData>>,
|
|
64
|
-
) => boolean,
|
|
65
|
-
): number => 0;
|
|
66
|
-
}
|
package/src/util/no-cache.md
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
`NoCache` is a cache implementation to use when no caching is wanted.
|
|
3
|
-
|
|
4
|
-
Use this with your request handler if you want to support server-side
|
|
5
|
-
rendering of your data requests, but want to ensure data is never cached
|
|
6
|
-
on the client-side.
|
|
7
|
-
|
|
8
|
-
This is better than having `shouldRefreshCache` always return `true` in the
|
|
9
|
-
handler as this ensures that cache space and memory are never used for the
|
|
10
|
-
requested data after hydration has finished.
|
|
11
|
-
|
|
12
|
-
The `ICache` interface is included below for reference in case you would like
|
|
13
|
-
to implement your own caching strategy.
|
|
14
|
-
|
|
15
|
-
```js static
|
|
16
|
-
interface ICache<TOptions, TData: ValidData> {
|
|
17
|
-
/**
|
|
18
|
-
* Stores a value in the cache for the given handler and options.
|
|
19
|
-
*/
|
|
20
|
-
store(
|
|
21
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
22
|
-
options: TOptions,
|
|
23
|
-
entry: CacheEntry<TData>,
|
|
24
|
-
): void;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Retrieves a value from the cache for the given handler and options.
|
|
28
|
-
*/
|
|
29
|
-
retrieve(
|
|
30
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
31
|
-
options: TOptions,
|
|
32
|
-
): ?$ReadOnly<CacheEntry<TData>>;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Remove the cached entry for the given handler and options.
|
|
36
|
-
*
|
|
37
|
-
* If the item exists in the cache, the cached entry is deleted and true
|
|
38
|
-
* is returned. Otherwise, this returns false.
|
|
39
|
-
*/
|
|
40
|
-
remove(
|
|
41
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
42
|
-
options: TOptions,
|
|
43
|
-
): boolean;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Remove all cached entries for the given handler that, optionally, match
|
|
47
|
-
* a given predicate.
|
|
48
|
-
*
|
|
49
|
-
* Returns the number of entries that were cleared from the cache.
|
|
50
|
-
*/
|
|
51
|
-
removeAll(
|
|
52
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
53
|
-
predicate?: (
|
|
54
|
-
key: string,
|
|
55
|
-
cachedEntry: $ReadOnly<CacheEntry<TData>>,
|
|
56
|
-
) => boolean,
|
|
57
|
-
): number;
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Use `NoCache` with your request handler if you want to support server-side
|
|
61
|
-
rendering of your data requests, but also want to ensure data is never cached
|
|
62
|
-
on the client-side.
|
|
63
|
-
|
|
64
|
-
This is better than having `shouldRefreshCache` always return `true` in the
|
|
65
|
-
handler as this ensures that cache space and memory are never used for the
|
|
66
|
-
requested data after hydration has finished.
|