-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make makeVar a global function instead of a method of InMemoryCache.
The makeVar method was originally attached to InMemoryCache so that we could call cache.broadcastWatches() whenever the variable was updated. See #5799 and #5976 for background. However, as a number of developers have reported, requiring access to an InMemoryCache to create a ReactiveVar can be awkward, since the code that calls makeVar may not be colocated with the code that creates the cache, and it is often desirable to create and initialize reactive variables before the cache has been created. As this commit shows, the ReactiveVar function can infer the current InMemoryCache from a contextual Slot, when called without arguments (that is, when reading the variable). When the variable is updated (by passing a new value to the ReactiveVar function), any caches that previously read the variable will be notified of the update. Since this logic happens at variable access time rather than variable creation time, makeVar can be a free-floating global function, importable directly from @apollo/client. This new system allows the variable to become associated with any number of InMemoryCache instances, whereas previously a given variable was only ever associated with one InMemoryCache. Note: when I say "any number" I very much mean to include zero, since a ReactiveVar that has not been associated with any caches yet can still be used as a container, and will not trigger any broadcasts when updated. The Slot class that makes this all work may seem like magic, but we have been using it ever since Apollo Client 2.5 (#3394, via the optimism library), so it has been amply battle-tested. This magic works.
- Loading branch information
Showing
9 changed files
with
92 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Slot } from "@wry/context"; | ||
import { dep } from "optimism"; | ||
import { InMemoryCache } from "./inMemoryCache"; | ||
|
||
export type ReactiveVar<T> = (newValue?: T) => T; | ||
|
||
const varDep = dep<ReactiveVar<any>>(); | ||
|
||
// Contextual Slot that acquires its value when custom read functions are | ||
// called in Policies#readField. | ||
export const cacheSlot = new Slot<InMemoryCache>(); | ||
|
||
export function makeVar<T>(value: T): ReactiveVar<T> { | ||
const caches = new Set<InMemoryCache>(); | ||
|
||
return function rv(newValue) { | ||
if (arguments.length > 0) { | ||
if (value !== newValue) { | ||
value = newValue!; | ||
varDep.dirty(rv); | ||
// Trigger broadcast for any caches that were previously involved | ||
// in reading this variable. | ||
caches.forEach(broadcast); | ||
} | ||
} else { | ||
// When reading from the variable, obtain the current InMemoryCache | ||
// from context via cacheSlot. This isn't entirely foolproof, but | ||
// it's the same system that powers varDep. | ||
const cache = cacheSlot.getValue(); | ||
if (cache) caches.add(cache); | ||
varDep(rv); | ||
} | ||
|
||
return value; | ||
}; | ||
} | ||
|
||
type Broadcastable = InMemoryCache & { | ||
// This method is protected in InMemoryCache, which we are ignoring, but | ||
// we still want some semblance of type safety when we call it. | ||
broadcastWatches: InMemoryCache["broadcastWatches"]; | ||
}; | ||
|
||
function broadcast(cache: Broadcastable) { | ||
cache.broadcastWatches(); | ||
} |