Skip to content

Commit

Permalink
[react-cache] Remove cache as argument to read
Browse files Browse the repository at this point in the history
Updated is API is `Resource.read(key)` instead of
`Resource.read(cache, key)`.

The cache is read from context using `readContext`.

This also removes cache invalidation entirely (other than the default
LRU mechanism), as well as the ability to have multiple caches. We'll
add it back once `Context.write` lands and we can implement it the
right way.

Since there's now only a single cache (the global one), we don't
actually need to use context yet, but I've added a dummy context
anyway so the user gets an error if they attempt to read outside the
render phase.
  • Loading branch information
acdlite committed Oct 16, 2018
1 parent 7685b55 commit 71f1677
Show file tree
Hide file tree
Showing 10 changed files with 659 additions and 685 deletions.
12 changes: 5 additions & 7 deletions packages/jest-react/src/JestReact.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,17 @@ function assertYieldsWereCleared(root) {
}

export function toFlushAndYield(root, expectedYields) {
assertYieldsWereCleared(root);
const actualYields = root.unstable_flushAll();
return captureAssertion(() => {
assertYieldsWereCleared(root);
const actualYields = root.unstable_flushAll();
expect(actualYields).toEqual(expectedYields);
});
}

export function toFlushAndYieldThrough(root, expectedYields) {
assertYieldsWereCleared(root);
const actualYields = root.unstable_flushNumberOfYields(expectedYields.length);
return captureAssertion(() => {
assertYieldsWereCleared(root);
const actualYields = root.unstable_flushNumberOfYields(
expectedYields.length,
);
expect(actualYields).toEqual(expectedYields);
});
}
Expand Down Expand Up @@ -76,8 +74,8 @@ export function toHaveYielded(ReactTestRenderer, expectedYields) {
}

export function toFlushAndThrow(root, ...rest) {
assertYieldsWereCleared(root);
return captureAssertion(() => {
assertYieldsWereCleared(root);
expect(() => {
root.unstable_flushAll();
}).toThrow(...rest);
Expand Down
142 changes: 142 additions & 0 deletions packages/react-cache/src/LRU.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import {unstable_scheduleCallback as scheduleCallback} from 'scheduler';

type Entry<T> = {|
value: T,
onDelete: () => mixed,
previous: Entry<T>,
next: Entry<T>,
|};

export function createLRU<T>(limit: number) {
let LIMIT = limit;

// Circular, doubly-linked list
let first: Entry<T> | null = null;
let size: number = 0;

let cleanUpIsScheduled: boolean = false;

function scheduleCleanUp() {
if (cleanUpIsScheduled === false && size > LIMIT) {
// The cache size exceeds the limit. Schedule a callback to delete the
// least recently used entries.
cleanUpIsScheduled = true;
scheduleCallback(cleanUp);
}
}

function cleanUp() {
cleanUpIsScheduled = false;
deleteLeastRecentlyUsedEntries(LIMIT);
}

function deleteLeastRecentlyUsedEntries(targetSize: number) {
// Delete entries from the cache, starting from the end of the list.
if (first !== null) {
const resolvedFirst: Entry<T> = (first: any);
let last = resolvedFirst.previous;
while (size > targetSize && last !== null) {
const onDelete = last.onDelete;
const previous = last.previous;
last.onDelete = (null: any);

// Remove from the list
last.previous = last.next = (null: any);
if (last === first) {
// Reached the head of the list.
first = last = null;
} else {
(first: any).previous = previous;
previous.next = (first: any);
last = previous;
}

size -= 1;

// Call the destroy method after removing the entry from the list. If it
// throws, the rest of cache will not be deleted, but it will be in a
// valid state.
onDelete();
}
}
}

function add(value: T, onDelete: () => mixed): Entry<T> {
const entry = {
value,
onDelete,
next: (null: any),
previous: (null: any),
};
if (first === null) {
entry.previous = entry.next = entry;
first = entry;
} else {
// Append to head
const last = first.previous;
last.next = entry;
entry.previous = last;

first.previous = entry;
entry.next = first;

first = entry;
}
size += 1;
return entry;
}

function update(entry: Entry<T>, newValue: T): void {
entry.value = newValue;
}

function access(entry: Entry<T>): T {
const next = entry.next;
if (next !== null) {
// Entry already cached
const resolvedFirst: Entry<T> = (first: any);
if (first !== entry) {
// Remove from current position
const previous = entry.previous;
previous.next = next;
next.previous = previous;

// Append to head
const last = resolvedFirst.previous;
last.next = entry;
entry.previous = last;

resolvedFirst.previous = entry;
entry.next = resolvedFirst;

first = entry;
}
} else {
// Cannot access a deleted entry
// TODO: Error? Warning?
}
scheduleCleanUp();
return entry.value;
}

function setLimit(newLimit: number) {
LIMIT = newLimit;
scheduleCleanUp();
}

return {
add,
update,
access,
setLimit,
};
}
Loading

0 comments on commit 71f1677

Please sign in to comment.