@khanacademy/wonder-blocks-data 4.0.0 → 5.0.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/CHANGELOG.md +6 -0
- package/dist/es/index.js +64 -45
- package/dist/index.js +109 -74
- package/package.json +1 -1
- package/src/__tests__/generated-snapshot.test.js +7 -7
- package/src/components/__tests__/data.test.js +11 -26
- package/src/components/__tests__/intercept-requests.test.js +58 -0
- package/src/components/data.js +4 -18
- package/src/components/intercept-context.js +4 -5
- package/src/components/intercept-requests.js +69 -0
- package/src/components/{intercept-data.md → intercept-requests.md} +18 -15
- package/src/hooks/__tests__/use-request-interception.test.js +255 -0
- package/src/hooks/use-request-interception.js +54 -0
- package/src/hooks/use-server-effect.js +3 -3
- package/src/index.js +2 -1
- package/src/components/__tests__/intercept-data.test.js +0 -63
- package/src/components/intercept-data.js +0 -66
|
@@ -22,7 +22,7 @@ import type {CachedResponse, ValidCacheData} from "../util/types.js";
|
|
|
22
22
|
* The asynchronous action is never invoked on the client-side.
|
|
23
23
|
*/
|
|
24
24
|
export const useServerEffect = <TData: ValidCacheData>(
|
|
25
|
-
|
|
25
|
+
requestId: string,
|
|
26
26
|
handler: () => Promise<?TData>,
|
|
27
27
|
hydrate: boolean = true,
|
|
28
28
|
): ?CachedResponse<TData> => {
|
|
@@ -31,14 +31,14 @@ export const useServerEffect = <TData: ValidCacheData>(
|
|
|
31
31
|
// This works in both hydration and SSR because the very first call to
|
|
32
32
|
// this will have cached data in those cases as it will be present on the
|
|
33
33
|
// initial render - and subsequent renders on the client it will be null.
|
|
34
|
-
const cachedResult = SsrCache.Default.getEntry<TData>(
|
|
34
|
+
const cachedResult = SsrCache.Default.getEntry<TData>(requestId);
|
|
35
35
|
|
|
36
36
|
// We only track data requests when we are server-side and we don't
|
|
37
37
|
// already have a result, as given by the cachedData (which is also the
|
|
38
38
|
// initial value for the result state).
|
|
39
39
|
const maybeTrack = useContext(TrackerContext);
|
|
40
40
|
if (cachedResult == null && Server.isServerSide()) {
|
|
41
|
-
maybeTrack?.(
|
|
41
|
+
maybeTrack?.(requestId, handler, hydrate);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
return cachedResult;
|
package/src/index.js
CHANGED
|
@@ -47,8 +47,9 @@ export const removeAllFromCache = (
|
|
|
47
47
|
|
|
48
48
|
export {default as TrackData} from "./components/track-data.js";
|
|
49
49
|
export {default as Data} from "./components/data.js";
|
|
50
|
-
export {default as
|
|
50
|
+
export {default as InterceptRequests} from "./components/intercept-requests.js";
|
|
51
51
|
export {useServerEffect} from "./hooks/use-server-effect.js";
|
|
52
|
+
export {useRequestInterception} from "./hooks/use-request-interception.js";
|
|
52
53
|
export {useSharedCache, clearSharedCache} from "./hooks/use-shared-cache.js";
|
|
53
54
|
export {ScopedInMemoryCache} from "./util/scoped-in-memory-cache.js";
|
|
54
55
|
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import {render} from "@testing-library/react";
|
|
4
|
-
|
|
5
|
-
import InterceptContext from "../intercept-context.js";
|
|
6
|
-
import InterceptData from "../intercept-data.js";
|
|
7
|
-
|
|
8
|
-
describe("InterceptData", () => {
|
|
9
|
-
afterEach(() => {
|
|
10
|
-
jest.resetAllMocks();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it("should update context with fulfillRequest method", () => {
|
|
14
|
-
// Arrange
|
|
15
|
-
const fakeHandler = () => Promise.resolve("data");
|
|
16
|
-
const props = {
|
|
17
|
-
handler: fakeHandler,
|
|
18
|
-
requestId: "ID",
|
|
19
|
-
};
|
|
20
|
-
const captureContextFn = jest.fn();
|
|
21
|
-
|
|
22
|
-
// Act
|
|
23
|
-
render(
|
|
24
|
-
<InterceptData {...props}>
|
|
25
|
-
<InterceptContext.Consumer>
|
|
26
|
-
{captureContextFn}
|
|
27
|
-
</InterceptContext.Consumer>
|
|
28
|
-
</InterceptData>,
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
// Assert
|
|
32
|
-
expect(captureContextFn).toHaveBeenCalledWith(
|
|
33
|
-
expect.objectContaining({
|
|
34
|
-
ID: props.handler,
|
|
35
|
-
}),
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should override parent InterceptData", () => {
|
|
40
|
-
// Arrange
|
|
41
|
-
const fulfillRequest1Fn = jest.fn();
|
|
42
|
-
const fulfillRequest2Fn = jest.fn();
|
|
43
|
-
const captureContextFn = jest.fn();
|
|
44
|
-
|
|
45
|
-
// Act
|
|
46
|
-
render(
|
|
47
|
-
<InterceptData handler={fulfillRequest1Fn} requestId="ID">
|
|
48
|
-
<InterceptData handler={fulfillRequest2Fn} requestId="ID">
|
|
49
|
-
<InterceptContext.Consumer>
|
|
50
|
-
{captureContextFn}
|
|
51
|
-
</InterceptContext.Consumer>
|
|
52
|
-
</InterceptData>
|
|
53
|
-
</InterceptData>,
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
// Assert
|
|
57
|
-
expect(captureContextFn).toHaveBeenCalledWith(
|
|
58
|
-
expect.objectContaining({
|
|
59
|
-
ID: fulfillRequest2Fn,
|
|
60
|
-
}),
|
|
61
|
-
);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import InterceptContext from "./intercept-context.js";
|
|
5
|
-
|
|
6
|
-
import type {ValidCacheData} from "../util/types.js";
|
|
7
|
-
|
|
8
|
-
type Props<TData: ValidCacheData> = {|
|
|
9
|
-
/**
|
|
10
|
-
* The ID of the request to intercept.
|
|
11
|
-
*/
|
|
12
|
-
requestId: string,
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Called to intercept and fulfill the request.
|
|
16
|
-
* If this returns null, the request will be fulfilled by the
|
|
17
|
-
* handler of the original request being intercepted.
|
|
18
|
-
*/
|
|
19
|
-
handler: () => ?Promise<?TData>,
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The children to render within this component. Any requests by `Data`
|
|
23
|
-
* components that use same ID as this component will be intercepted.
|
|
24
|
-
* (unless another `InterceptData` component overrides this one).
|
|
25
|
-
*/
|
|
26
|
-
children: React.Node,
|
|
27
|
-
|};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* This component provides a mechanism to intercept data requests.
|
|
31
|
-
* This is for use in testing.
|
|
32
|
-
*
|
|
33
|
-
* This component is not recommended for use in production code as it
|
|
34
|
-
* can prevent predictable functioning of the Wonder Blocks Data framework.
|
|
35
|
-
* One possible side-effect is that inflight requests from the interceptor could
|
|
36
|
-
* be picked up by `Data` component requests from outside the children of this
|
|
37
|
-
* component.
|
|
38
|
-
*
|
|
39
|
-
* These components do not chain. If a different `InterceptData` instance is
|
|
40
|
-
* rendered within this one that intercepts the same id, then that
|
|
41
|
-
* new instance will replace this interceptor for its children. All methods
|
|
42
|
-
* will be replaced.
|
|
43
|
-
*/
|
|
44
|
-
const InterceptData = <TData: ValidCacheData>({
|
|
45
|
-
requestId,
|
|
46
|
-
handler,
|
|
47
|
-
children,
|
|
48
|
-
}: Props<TData>): React.Node => {
|
|
49
|
-
const interceptMap = React.useContext(InterceptContext);
|
|
50
|
-
|
|
51
|
-
const updatedInterceptMap = React.useMemo(
|
|
52
|
-
() => ({
|
|
53
|
-
...interceptMap,
|
|
54
|
-
[requestId]: handler,
|
|
55
|
-
}),
|
|
56
|
-
[interceptMap, requestId, handler],
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
<InterceptContext.Provider value={updatedInterceptMap}>
|
|
61
|
-
{children}
|
|
62
|
-
</InterceptContext.Provider>
|
|
63
|
-
);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export default InterceptData;
|