Skip to content

Commit

Permalink
Add usePersistedStore and useSavedSnapshot
Browse files Browse the repository at this point in the history
- `useSavedSnapshot` loads a store snapshot from storage.
- `useSessionStorage` adds an `onSnapshot` listener that persists store snapshots in storage.
  • Loading branch information
eatyourgreens committed Mar 3, 2022
1 parent 735c2bb commit ddcff57
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 57 deletions.
16 changes: 16 additions & 0 deletions packages/lib-classifier/src/components/Classifier/hooks/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ Uses the `useStore` hook, under the hood, to create a new store from a saved sna
const { classifierStore, loaded } = useHydratedStore({ authClient, client }, enableStorage = false, storageKey)
```

## useSavedSnapshot

Asynchronously load a store snapshot from session storage.

```js
const initialState = useSavedSnapshot(enableStorage, storageKey)
```

## useSessionStorage

Wrap a store in an [`onSnapshot`](https://mobx-state-tree.js.org/API/#onsnapshot) listener, which writes snapshots asynchronously to session storage. Returns true when complete, which allows us to wait on store initialisation before adding new data to the store.

```js
const loaded = useSessionStorage(cachePanoptesData, classifierStore, storageKey)
```

## useStore

Create a `mobx-state-tree` store, using the Panoptes API clients and an optional snapshot. Adapted from [the NextJS example](https://github.com/vercel/next.js/blob/5201cdbaeaa72b54badc8f929ddc73c09f414dc4/examples/with-mobx-state-tree/store.js#L49-L52), which is also used in `app-project`. `initialState` must be a valid store snapshot.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { default as useHydratedStore } from './useHydratedStore'
export { default as useSavedSnapshot } from './useSavedSnapshot'
export { default as useSessionStorage } from './useSessionStorage'
export { default as useStore } from './useStore'
export { default as useWorkflowSnapshot } from './useWorkflowSnapshot'
Original file line number Diff line number Diff line change
@@ -1,53 +1,12 @@
import { useEffect, useState } from 'react'

import { asyncSessionStorage, persist } from '@helpers'
import { useStore } from './'

async function hydrateStore(storageKey) {
let snapshot = {}
try {
snapshot = await loadSnapshot(storageKey, asyncSessionStorage)
console.log('store loaded from local storage')
} catch (error) {
console.log('store snapshot error.')
console.error(error)
}
return snapshot
}

async function loadSnapshot(storageKey, storage) {
const data = await storage.getItem(storageKey)
const snapshot = JSON.parse(data)
return snapshot
}
import { useSavedSnapshot, useSessionStorage, useStore } from './'

export default function useHydratedStore({ authClient, client }, cachePanoptesData = false, storageKey) {
const [initialState, setInitialState] = useState(null)
const [loaded, setLoaded] = useState(false)
// Asynchronously load a store snapshot from an external source.
const initialState = useSavedSnapshot(cachePanoptesData, storageKey)
// Create a new store from the initialState snapshot.
const classifierStore = useStore({ authClient, client, initialState })

async function onStoreCreated() {
if (!loaded && classifierStore && cachePanoptesData) {
await persist(storageKey, classifierStore, { storage: asyncSessionStorage })
}
setLoaded(true)
}

useEffect(() => {
onStoreCreated()
}, [cachePanoptesData, classifierStore, loaded])

async function getInitialState() {
let _initialState = {}
if (cachePanoptesData) {
_initialState = await hydrateStore(storageKey)
}
setInitialState(_initialState)
}

useEffect(() => {
getInitialState()
}, [])
// Write store snapshots to session storage.
const loaded = useSessionStorage(cachePanoptesData, classifierStore, storageKey)

return { classifierStore, loaded }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useEffect, useState } from 'react'

import { asyncSessionStorage } from '@helpers'

async function loadSnapshot(storageKey, storage) {
const data = await storage.getItem(storageKey)
const snapshot = JSON.parse(data)
return snapshot
}

async function hydrateStore(storageKey) {
let snapshot = {}
try {
snapshot = await loadSnapshot(storageKey, asyncSessionStorage)
console.log('store loaded from local storage')
} catch (error) {
console.log('store snapshot error.')
console.error(error)
}
return snapshot
}

export default function useSavedSnapshot(enableStorage, storageKey) {
const [initialState, setInitialState] = useState(null)

async function getInitialState() {
let _initialState = {}
if (enableStorage) {
_initialState = await hydrateStore(storageKey)
}
setInitialState(_initialState)
}

useEffect(() => {
getInitialState()
}, [storageKey])

return initialState
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { addDisposer, onSnapshot } from 'mobx-state-tree'
import { useEffect, useState } from 'react'

import { asyncSessionStorage } from '@helpers'

function persist(storageKey, store, storage) {
function _saveSnapshot(snapshot) {
const data = JSON.stringify(snapshot)
storage.setItem(storageKey, data)
}
const snapshotDisposer = onSnapshot(store, _saveSnapshot)
return addDisposer(store, snapshotDisposer)
}

export default function useSessionStorage(enableStorage, store, storageKey) {
const [loaded, setLoaded] = useState(false)

useEffect(() => {
if (!loaded && store && enableStorage) {
persist(storageKey, store, asyncSessionStorage)
}
setLoaded(true)
}, [enableStorage, store, loaded])

return loaded
}
1 change: 0 additions & 1 deletion packages/lib-classifier/src/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ export { featureDetection }
export { default as findLocationsByMediaType } from './findLocationsByMediaType'
export { default as formatTimeStamp } from './formatTimeStamp'
export { default as layouts } from './layouts'
export { default as persist } from './persist'
export { default as shownMarks } from './shownMarks'
export { default as subjectsSeenThisSession } from './subjectsSeenThisSession'
export { default as subjectViewers } from './subjectViewers'
Expand Down
1 change: 0 additions & 1 deletion packages/lib-classifier/src/helpers/persist/index.js

This file was deleted.

8 changes: 0 additions & 8 deletions packages/lib-classifier/src/helpers/persist/persist.js

This file was deleted.

0 comments on commit ddcff57

Please sign in to comment.