@samitouri / QOS-React-2 / commits / 2cf9063318

createResource returns an object with methods instead of a read function (#12304)

Changes `createResource` to return an object with `read` and `preload` methods. Future methods may include `set`, `subscribe`, `invalidate`, and so on.

Andrew Clark committed Feb 28, 2018 at 10:17 UTC 2cf9063318014dcc9a4839bb15ebb89a55198fce
2 files changed +58 -57
packages/simple-cache-provider/src/SimpleCacheProvider.js
+36 -35
@@ -225,11 +225,11 @@ if (__DEV__) {
225 }
226
227 type primitive = string | number | boolean | void | null;
228 -type ResourceReader<K, V> = (Cache, K) => V;
228
230 -type Resource<K, V> = ResourceReader<K, V> & {
229 +type Resource<K, V> = {|
230 + read(Cache, K): V,
231 preload(cache: Cache, key: K): void,
232 -};
232 +|};
233
234 // These declarations are used to express function overloading. I wish there
235 // were a more elegant way to do this in the function definition itself.
@@ -252,43 +252,44 @@ export function createResource<V, K, H: primitive>(
252 loadResource: K => Promise<V>,
253 hash: K => H,
254 ): Resource<K, V> {
255 - // The read function itself serves as the resource type.
256 - function read(cache, key) {
257 - if (__DEV__) {
258 - warning(
259 - isCache(cache),
260 - 'read(): The first argument must be a cache. Instead received: %s',
261 - cache,
262 - );
263 - }
264 - if (hash === undefined) {
255 + const resource = {
256 + read(cache, key) {
257 if (__DEV__) {
266 - warnIfNonPrimitiveKey(key, 'read');
258 + warning(
259 + isCache(cache),
260 + 'read(): The first argument must be a cache. Instead received: %s',
261 + cache,
262 + );
263 }
268 - return cache.read(read, key, loadResource, key);
269 - }
270 - const hashedKey = hash(key);
271 - return cache.read(read, hashedKey, loadResource, key);
272 - }
273 - read.preload = function(cache, key) {
274 - if (__DEV__) {
275 - warning(
276 - isCache(cache),
277 - 'preload(): The first argument must be a cache. Instead received: %s',
278 - cache,
279 - );
280 - }
281 - if (hash === undefined) {
264 + if (hash === undefined) {
265 + if (__DEV__) {
266 + warnIfNonPrimitiveKey(key, 'read');
267 + }
268 + return cache.read(resource, key, loadResource, key);
269 + }
270 + const hashedKey = hash(key);
271 + return cache.read(resource, hashedKey, loadResource, key);
272 + },
273 + preload(cache, key) {
274 if (__DEV__) {
283 - warnIfNonPrimitiveKey(key, 'preload');
275 + warning(
276 + isCache(cache),
277 + 'preload(): The first argument must be a cache. Instead received: %s',
278 + cache,
279 + );
280 }
285 - cache.preload(read, key, loadResource, key);
286 - return;
287 - }
288 - const hashedKey = hash(key);
289 - cache.preload(read, hashedKey, loadResource, key);
281 + if (hash === undefined) {
282 + if (__DEV__) {
283 + warnIfNonPrimitiveKey(key, 'preload');
284 + }
285 + cache.preload(resource, key, loadResource, key);
286 + return;
287 + }
288 + const hashedKey = hash(key);
289 + cache.preload(resource, hashedKey, loadResource, key);
290 + },
291 };
291 - return read;
292 + return resource;
293 }
294
295 // Global cache has no eviction policy (except for, ya know, a browser refresh).
packages/simple-cache-provider/src/__tests__/SimpleCacheProvider-test.js
+22 -22
@@ -23,18 +23,18 @@ describe('SimpleCacheProvider', () => {
23 function loadUpperCase(text) {
24 return Promise.resolve(text.toUpperCase());
25 }
26 - const readUpperCase = createResource(loadUpperCase);
26 + const UpperCase = createResource(loadUpperCase);
27 const cache = createCache();
28
29 let suspender;
30 try {
31 - readUpperCase(cache, 'hello');
31 + UpperCase.read(cache, 'hello');
32 } catch (v) {
33 suspender = v;
34 }
35
36 await suspender;
37 - const result = readUpperCase(cache, 'hello');
37 + const result = UpperCase.read(cache, 'hello');
38 expect(result).toBe('HELLO');
39 });
40
@@ -52,12 +52,12 @@ describe('SimpleCacheProvider', () => {
52 return Promise.resolve(text.toUpperCase());
53 }
54 }
55 - const readUpperCase = createResource(loadUpperCase);
55 + const UpperCase = createResource(loadUpperCase);
56 const cache = createCache();
57
58 let suspender;
59 try {
60 - readUpperCase(cache, 'hello');
60 + UpperCase.read(cache, 'hello');
61 } catch (v) {
62 suspender = v;
63 }
@@ -68,17 +68,17 @@ describe('SimpleCacheProvider', () => {
68 } catch (e) {
69 error = e;
70 }
71 - expect(() => readUpperCase(cache, 'hello')).toThrow(error);
71 + expect(() => UpperCase.read(cache, 'hello')).toThrow(error);
72 expect(error.message).toBe('oh no');
73
74 // On a subsequent read, it should still throw.
75 try {
76 - readUpperCase(cache, 'hello');
76 + UpperCase.read(cache, 'hello');
77 } catch (v) {
78 suspender = v;
79 }
80 await suspender;
81 - expect(() => readUpperCase(cache, 'hello')).toThrow(error);
81 + expect(() => UpperCase.read(cache, 'hello')).toThrow(error);
82 expect(error.message).toBe('oh no');
83 });
84
@@ -88,13 +88,13 @@ describe('SimpleCacheProvider', () => {
88 function loadUpperCase(text) {
89 return Promise.resolve(text.toUpperCase());
90 }
91 - const readUpperCase = createResource(loadUpperCase);
91 + const UpperCase = createResource(loadUpperCase);
92 const cache = createCache();
93
94 - readUpperCase.preload(cache, 'hello');
94 + UpperCase.preload(cache, 'hello');
95 // Wait for next tick
96 await Promise.resolve();
97 - const result = readUpperCase(cache, 'hello');
97 + const result = UpperCase.read(cache, 'hello');
98 expect(result).toBe('HELLO');
99 });
100
@@ -104,14 +104,14 @@ describe('SimpleCacheProvider', () => {
104 function loadUpperCase(text) {
105 return Promise.reject(new Error('uh oh'));
106 }
107 - const readUpperCase = createResource(loadUpperCase);
107 + const UpperCase = createResource(loadUpperCase);
108 const cache = createCache();
109
110 - readUpperCase.preload(cache, 'hello');
110 + UpperCase.preload(cache, 'hello');
111 // Wait for next tick
112 await Promise.resolve();
113
114 - expect(() => readUpperCase(cache, 'hello')).toThrow('uh oh');
114 + expect(() => UpperCase.read(cache, 'hello')).toThrow('uh oh');
115 });
116
117 it('accepts custom hash function', async () => {
@@ -123,18 +123,18 @@ describe('SimpleCacheProvider', () => {
123 function hash([a, b]) {
124 return `${a + b}`;
125 }
126 - const readSum = createResource(loadSum, hash);
126 + const Sum = createResource(loadSum, hash);
127 const cache = createCache();
128
129 - readSum.preload(cache, [5, 5]);
130 - readSum.preload(cache, [1, 2]);
129 + Sum.preload(cache, [5, 5]);
130 + Sum.preload(cache, [1, 2]);
131 await Promise.resolve();
132
133 - expect(readSum(cache, [5, 5])).toEqual(10);
134 - expect(readSum(cache, [1, 2])).toEqual(3);
133 + expect(Sum.read(cache, [5, 5])).toEqual(10);
134 + expect(Sum.read(cache, [1, 2])).toEqual(3);
135 // The fact that the next line returns synchronously and doesn't throw, even
136 // though [3, 7] was not preloaded, proves that the hashing function works.
137 - expect(readSum(cache, [3, 7])).toEqual(10);
137 + expect(Sum.read(cache, [3, 7])).toEqual(10);
138 });
139
140 it('warns if resourceType is a string or number', () => {
@@ -171,11 +171,11 @@ describe('SimpleCacheProvider', () => {
171 return Promise.resolve(a + b);
172 }
173
174 - const readSum = createResource(loadSum);
174 + const Sum = createResource(loadSum);
175 const cache = createCache();
176
177 function fn() {
178 - readSum.preload(cache, [5, 5]);
178 + Sum.preload(cache, [5, 5]);
179 }
180
181 if (__DEV__) {