mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
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.
This commit is contained in:
@@ -225,11 +225,11 @@ if (__DEV__) {
|
||||
}
|
||||
|
||||
type primitive = string | number | boolean | void | null;
|
||||
type ResourceReader<K, V> = (Cache, K) => V;
|
||||
|
||||
type Resource<K, V> = ResourceReader<K, V> & {
|
||||
type Resource<K, V> = {|
|
||||
read(Cache, K): V,
|
||||
preload(cache: Cache, key: K): void,
|
||||
};
|
||||
|};
|
||||
|
||||
// These declarations are used to express function overloading. I wish there
|
||||
// were a more elegant way to do this in the function definition itself.
|
||||
@@ -252,43 +252,44 @@ export function createResource<V, K, H: primitive>(
|
||||
loadResource: K => Promise<V>,
|
||||
hash: K => H,
|
||||
): Resource<K, V> {
|
||||
// The read function itself serves as the resource type.
|
||||
function read(cache, key) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
isCache(cache),
|
||||
'read(): The first argument must be a cache. Instead received: %s',
|
||||
cache,
|
||||
);
|
||||
}
|
||||
if (hash === undefined) {
|
||||
const resource = {
|
||||
read(cache, key) {
|
||||
if (__DEV__) {
|
||||
warnIfNonPrimitiveKey(key, 'read');
|
||||
warning(
|
||||
isCache(cache),
|
||||
'read(): The first argument must be a cache. Instead received: %s',
|
||||
cache,
|
||||
);
|
||||
}
|
||||
return cache.read(read, key, loadResource, key);
|
||||
}
|
||||
const hashedKey = hash(key);
|
||||
return cache.read(read, hashedKey, loadResource, key);
|
||||
}
|
||||
read.preload = function(cache, key) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
isCache(cache),
|
||||
'preload(): The first argument must be a cache. Instead received: %s',
|
||||
cache,
|
||||
);
|
||||
}
|
||||
if (hash === undefined) {
|
||||
if (hash === undefined) {
|
||||
if (__DEV__) {
|
||||
warnIfNonPrimitiveKey(key, 'read');
|
||||
}
|
||||
return cache.read(resource, key, loadResource, key);
|
||||
}
|
||||
const hashedKey = hash(key);
|
||||
return cache.read(resource, hashedKey, loadResource, key);
|
||||
},
|
||||
preload(cache, key) {
|
||||
if (__DEV__) {
|
||||
warnIfNonPrimitiveKey(key, 'preload');
|
||||
warning(
|
||||
isCache(cache),
|
||||
'preload(): The first argument must be a cache. Instead received: %s',
|
||||
cache,
|
||||
);
|
||||
}
|
||||
cache.preload(read, key, loadResource, key);
|
||||
return;
|
||||
}
|
||||
const hashedKey = hash(key);
|
||||
cache.preload(read, hashedKey, loadResource, key);
|
||||
if (hash === undefined) {
|
||||
if (__DEV__) {
|
||||
warnIfNonPrimitiveKey(key, 'preload');
|
||||
}
|
||||
cache.preload(resource, key, loadResource, key);
|
||||
return;
|
||||
}
|
||||
const hashedKey = hash(key);
|
||||
cache.preload(resource, hashedKey, loadResource, key);
|
||||
},
|
||||
};
|
||||
return read;
|
||||
return resource;
|
||||
}
|
||||
|
||||
// Global cache has no eviction policy (except for, ya know, a browser refresh).
|
||||
|
||||
@@ -23,18 +23,18 @@ describe('SimpleCacheProvider', () => {
|
||||
function loadUpperCase(text) {
|
||||
return Promise.resolve(text.toUpperCase());
|
||||
}
|
||||
const readUpperCase = createResource(loadUpperCase);
|
||||
const UpperCase = createResource(loadUpperCase);
|
||||
const cache = createCache();
|
||||
|
||||
let suspender;
|
||||
try {
|
||||
readUpperCase(cache, 'hello');
|
||||
UpperCase.read(cache, 'hello');
|
||||
} catch (v) {
|
||||
suspender = v;
|
||||
}
|
||||
|
||||
await suspender;
|
||||
const result = readUpperCase(cache, 'hello');
|
||||
const result = UpperCase.read(cache, 'hello');
|
||||
expect(result).toBe('HELLO');
|
||||
});
|
||||
|
||||
@@ -52,12 +52,12 @@ describe('SimpleCacheProvider', () => {
|
||||
return Promise.resolve(text.toUpperCase());
|
||||
}
|
||||
}
|
||||
const readUpperCase = createResource(loadUpperCase);
|
||||
const UpperCase = createResource(loadUpperCase);
|
||||
const cache = createCache();
|
||||
|
||||
let suspender;
|
||||
try {
|
||||
readUpperCase(cache, 'hello');
|
||||
UpperCase.read(cache, 'hello');
|
||||
} catch (v) {
|
||||
suspender = v;
|
||||
}
|
||||
@@ -68,17 +68,17 @@ describe('SimpleCacheProvider', () => {
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(() => readUpperCase(cache, 'hello')).toThrow(error);
|
||||
expect(() => UpperCase.read(cache, 'hello')).toThrow(error);
|
||||
expect(error.message).toBe('oh no');
|
||||
|
||||
// On a subsequent read, it should still throw.
|
||||
try {
|
||||
readUpperCase(cache, 'hello');
|
||||
UpperCase.read(cache, 'hello');
|
||||
} catch (v) {
|
||||
suspender = v;
|
||||
}
|
||||
await suspender;
|
||||
expect(() => readUpperCase(cache, 'hello')).toThrow(error);
|
||||
expect(() => UpperCase.read(cache, 'hello')).toThrow(error);
|
||||
expect(error.message).toBe('oh no');
|
||||
});
|
||||
|
||||
@@ -88,13 +88,13 @@ describe('SimpleCacheProvider', () => {
|
||||
function loadUpperCase(text) {
|
||||
return Promise.resolve(text.toUpperCase());
|
||||
}
|
||||
const readUpperCase = createResource(loadUpperCase);
|
||||
const UpperCase = createResource(loadUpperCase);
|
||||
const cache = createCache();
|
||||
|
||||
readUpperCase.preload(cache, 'hello');
|
||||
UpperCase.preload(cache, 'hello');
|
||||
// Wait for next tick
|
||||
await Promise.resolve();
|
||||
const result = readUpperCase(cache, 'hello');
|
||||
const result = UpperCase.read(cache, 'hello');
|
||||
expect(result).toBe('HELLO');
|
||||
});
|
||||
|
||||
@@ -104,14 +104,14 @@ describe('SimpleCacheProvider', () => {
|
||||
function loadUpperCase(text) {
|
||||
return Promise.reject(new Error('uh oh'));
|
||||
}
|
||||
const readUpperCase = createResource(loadUpperCase);
|
||||
const UpperCase = createResource(loadUpperCase);
|
||||
const cache = createCache();
|
||||
|
||||
readUpperCase.preload(cache, 'hello');
|
||||
UpperCase.preload(cache, 'hello');
|
||||
// Wait for next tick
|
||||
await Promise.resolve();
|
||||
|
||||
expect(() => readUpperCase(cache, 'hello')).toThrow('uh oh');
|
||||
expect(() => UpperCase.read(cache, 'hello')).toThrow('uh oh');
|
||||
});
|
||||
|
||||
it('accepts custom hash function', async () => {
|
||||
@@ -123,18 +123,18 @@ describe('SimpleCacheProvider', () => {
|
||||
function hash([a, b]) {
|
||||
return `${a + b}`;
|
||||
}
|
||||
const readSum = createResource(loadSum, hash);
|
||||
const Sum = createResource(loadSum, hash);
|
||||
const cache = createCache();
|
||||
|
||||
readSum.preload(cache, [5, 5]);
|
||||
readSum.preload(cache, [1, 2]);
|
||||
Sum.preload(cache, [5, 5]);
|
||||
Sum.preload(cache, [1, 2]);
|
||||
await Promise.resolve();
|
||||
|
||||
expect(readSum(cache, [5, 5])).toEqual(10);
|
||||
expect(readSum(cache, [1, 2])).toEqual(3);
|
||||
expect(Sum.read(cache, [5, 5])).toEqual(10);
|
||||
expect(Sum.read(cache, [1, 2])).toEqual(3);
|
||||
// The fact that the next line returns synchronously and doesn't throw, even
|
||||
// though [3, 7] was not preloaded, proves that the hashing function works.
|
||||
expect(readSum(cache, [3, 7])).toEqual(10);
|
||||
expect(Sum.read(cache, [3, 7])).toEqual(10);
|
||||
});
|
||||
|
||||
it('warns if resourceType is a string or number', () => {
|
||||
@@ -171,11 +171,11 @@ describe('SimpleCacheProvider', () => {
|
||||
return Promise.resolve(a + b);
|
||||
}
|
||||
|
||||
const readSum = createResource(loadSum);
|
||||
const Sum = createResource(loadSum);
|
||||
const cache = createCache();
|
||||
|
||||
function fn() {
|
||||
readSum.preload(cache, [5, 5]);
|
||||
Sum.preload(cache, [5, 5]);
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
|
||||
Reference in New Issue
Block a user