From f315c7c77f51d57a5418dafdc7fa350ac3098860 Mon Sep 17 00:00:00 2001 From: Pedro Bern Date: Sun, 11 Oct 2020 10:21:15 -0300 Subject: [PATCH 1/8] feat: reactiveVars storage options --- src/cache/inmemory/reactiveVars.ts | 56 ++++++++++++++++++++-- src/utilities/common/__tests__/isString.ts | 11 +++++ src/utilities/common/isString.ts | 3 ++ 3 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 src/utilities/common/__tests__/isString.ts create mode 100644 src/utilities/common/isString.ts diff --git a/src/cache/inmemory/reactiveVars.ts b/src/cache/inmemory/reactiveVars.ts index f063bb03546..9df0898dcce 100644 --- a/src/cache/inmemory/reactiveVars.ts +++ b/src/cache/inmemory/reactiveVars.ts @@ -2,6 +2,7 @@ import { Slot } from "@wry/context"; import { dep } from "optimism"; import { InMemoryCache } from "./inMemoryCache"; import { ApolloCache } from '../../core'; +import { isString } from "../../utilities/common/isString" export interface ReactiveVar { (newValue?: T): T; @@ -27,9 +28,16 @@ function consumeAndIterate(set: Set, callback: (item: T) => any) { items.forEach(callback); } -export function makeVar(value: T): ReactiveVar { - const caches = new Set>(); - const listeners = new Set>(); +// makeVar overload signatures +export function makeVar(value: T): ReactiveVar +export function makeVar( + value: T, + config: PersistenceConfig +): [ReactiveVar, () => Promise] + +export function makeVar(value: T, config?: PersistenceConfig) { + const caches = new Set>() + const listeners = new Set>() const rv: ReactiveVar = function (newValue) { if (arguments.length > 0) { @@ -44,6 +52,15 @@ export function makeVar(value: T): ReactiveVar { caches.forEach(broadcast); // Finally, notify any listeners added via rv.onNextChange. consumeAndIterate(listeners, listener => listener(value)); + // Run persistence options + try { + config?.storage.setItem( + config.storageKey, + cleanValueToSetStorage(value) + ); + } catch { + // pass + } } } else { // When reading from the variable, obtain the current cache from @@ -64,7 +81,22 @@ export function makeVar(value: T): ReactiveVar { }; }; - return rv; + if (!config) return rv; + + const restore = async () => { + // Set reactiveVar to previous value from storage, + // if there is no previous value, do nothing. + try { + const previousValue = await config.storage.getItem(config.storageKey) + if (previousValue) { + rv(isString(value) ? previousValue : JSON.parse(previousValue)) + } + } catch { + // pass + } + } + + return [rv, restore] } type Broadcastable = ApolloCache & { @@ -78,3 +110,19 @@ function broadcast(cache: Broadcastable) { cache.broadcastWatches(); } } + +// Persistence utils + +export interface PersistentStorage { + getItem: (key: string) => Promise; + setItem: (key: string, data: string) => Promise; +} + +export type PersistenceConfig = { + storage: PersistentStorage; + storageKey: string; +} + +function cleanValueToSetStorage(value: any): string { + return isString(value) ? value : JSON.stringify(value); +} diff --git a/src/utilities/common/__tests__/isString.ts b/src/utilities/common/__tests__/isString.ts new file mode 100644 index 00000000000..34e4ba394bf --- /dev/null +++ b/src/utilities/common/__tests__/isString.ts @@ -0,0 +1,11 @@ +import { isString } from '../isString'; + +describe('isString', () => { + it('should indetify strings', () => { + const someString = isString("somestring") + const notStrings = [{}, [], 0, undefined,null] + + expect(someString).toEqual(true); + notStrings.forEach(f => expect(isString(f)).toEqual(false)); + }); +}); diff --git a/src/utilities/common/isString.ts b/src/utilities/common/isString.ts new file mode 100644 index 00000000000..52ecabe9f21 --- /dev/null +++ b/src/utilities/common/isString.ts @@ -0,0 +1,3 @@ +export function isString(value: any) { + return Object.prototype.toString.call(value) === '[object String]' +} From d4857045eee9a117e934de62ad5cdb94bdc07b86 Mon Sep 17 00:00:00 2001 From: Pedro Bern Date: Mon, 12 Oct 2020 11:23:45 -0300 Subject: [PATCH 2/8] docs: reactiveVars persistence options --- .../source/local-state/reactive-variables.mdx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/source/local-state/reactive-variables.mdx b/docs/source/local-state/reactive-variables.mdx index 318bedc7e39..0a1db86588c 100644 --- a/docs/source/local-state/reactive-variables.mdx +++ b/docs/source/local-state/reactive-variables.mdx @@ -21,6 +21,28 @@ This code creates a reactive variable with an empty array as its initial value. > **Important:** The return value of `makeVar` is a _function_ that you call to read or modify your reactive variable's value. +### Persistence options + +Optionally you can make a persistent reactive variable: + +```js +import { makeVar } from "@apollo/client"; +import AsyncStorage from "@react-native-community/async-storage"; + +const options = { + storage: AsyncStorage, + storageKey: "@cartItems" +}; + +const [cartItems, restoreCartItems] = makeVar([], options); + +// Restoring the variable before Apollo setup: +// For example, you can run this inside componentDidMount +// or useEffect hook, and render a loading screen while +// it's not ready. +await restoreCartItems(); +``` + ## Reading To read the value of your reactive variable, call the function returned by `makeVar` with zero arguments: From 066dac8be5169e9246fb5ab046e7883de5685d42 Mon Sep 17 00:00:00 2001 From: Pedro Bern Date: Mon, 12 Oct 2020 18:28:46 -0300 Subject: [PATCH 3/8] docs: restore reacive vars example --- .../source/local-state/reactive-variables.mdx | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/source/local-state/reactive-variables.mdx b/docs/source/local-state/reactive-variables.mdx index 0a1db86588c..2729e7fe5f7 100644 --- a/docs/source/local-state/reactive-variables.mdx +++ b/docs/source/local-state/reactive-variables.mdx @@ -36,11 +36,27 @@ const options = { const [cartItems, restoreCartItems] = makeVar([], options); -// Restoring the variable before Apollo setup: -// For example, you can run this inside componentDidMount -// or useEffect hook, and render a loading screen while -// it's not ready. -await restoreCartItems(); +// Example of restoring the variables +function App() { + const [ready, setReady] = React.useState(false); + + React.useEffect(() => { + const restoreReactiveVars = async () => { + await restoreCartItems() + // restore more variables here + setReady(true) + } + restoreReactiveVars() + }, []) + + if (!ready) return + + return ( + + ... + + ) +} ``` ## Reading From 5f5c8b51145621c8bc5e5b98d855b310f97bd2b7 Mon Sep 17 00:00:00 2001 From: Pedro Bern Date: Mon, 12 Oct 2020 18:28:46 -0300 Subject: [PATCH 4/8] docs: restore reactiveVars example --- .../source/local-state/reactive-variables.mdx | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/source/local-state/reactive-variables.mdx b/docs/source/local-state/reactive-variables.mdx index 0a1db86588c..2729e7fe5f7 100644 --- a/docs/source/local-state/reactive-variables.mdx +++ b/docs/source/local-state/reactive-variables.mdx @@ -36,11 +36,27 @@ const options = { const [cartItems, restoreCartItems] = makeVar([], options); -// Restoring the variable before Apollo setup: -// For example, you can run this inside componentDidMount -// or useEffect hook, and render a loading screen while -// it's not ready. -await restoreCartItems(); +// Example of restoring the variables +function App() { + const [ready, setReady] = React.useState(false); + + React.useEffect(() => { + const restoreReactiveVars = async () => { + await restoreCartItems() + // restore more variables here + setReady(true) + } + restoreReactiveVars() + }, []) + + if (!ready) return + + return ( + + ... + + ) +} ``` ## Reading From b2e4ee769fa41261fe0824689fef21d94e27a389 Mon Sep 17 00:00:00 2001 From: Pedro Bern Date: Fri, 16 Oct 2020 19:34:02 -0300 Subject: [PATCH 5/8] fix: typo --- src/cache/inmemory/reactiveVars.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cache/inmemory/reactiveVars.ts b/src/cache/inmemory/reactiveVars.ts index 9df0898dcce..79c1fd94ac5 100644 --- a/src/cache/inmemory/reactiveVars.ts +++ b/src/cache/inmemory/reactiveVars.ts @@ -32,10 +32,10 @@ function consumeAndIterate(set: Set, callback: (item: T) => any) { export function makeVar(value: T): ReactiveVar export function makeVar( value: T, - config: PersistenceConfig + config: PersistenceConfig ): [ReactiveVar, () => Promise] -export function makeVar(value: T, config?: PersistenceConfig) { +export function makeVar(value: T, config?: PersistenceConfig) { const caches = new Set>() const listeners = new Set>() @@ -118,7 +118,7 @@ export interface PersistentStorage { setItem: (key: string, data: string) => Promise; } -export type PersistenceConfig = { +export type PersistenceConfig = { storage: PersistentStorage; storageKey: string; } From 40bdb1efb2cb222d24dbc73c34ad8aecef655abf Mon Sep 17 00:00:00 2001 From: Pedro Bern Date: Thu, 22 Oct 2020 07:56:58 -0300 Subject: [PATCH 6/8] fix: circleCI max size --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ac83de095b..aff89f1e73a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ { "name": "apollo-client", "path": "./dist/apollo-client.cjs.min.js", - "maxSize": "24.7 kB" + "maxSize": "24.85 kB" } ], "peerDependencies": { From f922c95316658bb95834941a904e2e1cfb6c19aa Mon Sep 17 00:00:00 2001 From: Pedro Bern Date: Thu, 22 Oct 2020 08:01:17 -0300 Subject: [PATCH 7/8] fix: circleCI max size --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aff89f1e73a..c8db3ead4c6 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ { "name": "apollo-client", "path": "./dist/apollo-client.cjs.min.js", - "maxSize": "24.85 kB" + "maxSize": "24.86 kB" } ], "peerDependencies": { From 1e0dd96726017673218b4274d207b0f093ef57bb Mon Sep 17 00:00:00 2001 From: pedrobern Date: Thu, 22 Oct 2020 08:59:56 -0300 Subject: [PATCH 8/8] fix: typo Co-authored-by: Steven Rathbauer --- src/utilities/common/__tests__/isString.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utilities/common/__tests__/isString.ts b/src/utilities/common/__tests__/isString.ts index 34e4ba394bf..b4abd13a0f7 100644 --- a/src/utilities/common/__tests__/isString.ts +++ b/src/utilities/common/__tests__/isString.ts @@ -1,7 +1,7 @@ import { isString } from '../isString'; describe('isString', () => { - it('should indetify strings', () => { + it('should identify strings', () => { const someString = isString("somestring") const notStrings = [{}, [], 0, undefined,null]