Skip to main content

Plugin Storage

HookStorage typeTypeDescription
usePluginStorage (deprecated) usePluginDeviceStorelocal{ getAllKeys, getString, setString, remove, clearAll }Allows you to store and retrieve data from a local only storage.
User based storage
usePluginUserStoreValueremote{ data, set, isLoading, error, refresh, remove }Allows you to manage your plugin's user based storage without the hassle of making requests yourself. It accepts a generic type which will define the stored value's type. Additionally the hook also accept a dependency array as the second parameter.
usePluginUserStoreremote{ get, set, delete }Provides functions that allow you to make requests to the Reach API to manage your plugin's user based storage.
Subscription based storage
usePluginSubscriptionStoreValueremote{ data, set, isLoading, error, refresh, remove }Allows you to manage your plugin's subscription based storage without the hassle of making requests yourself. It accepts a generic type which will define the stored value's type. Additionally the hook also accept a dependency array as the second parameter.
usePluginSubscriptionStoreremote{ get, set, delete }Provides functions that allow you to make requests to the Reach API to manage your plugin's subscription based storage.

Local Storage#

When you're developing a Reach Plugin you sometimes need to store some data in a simple key/value persistent store. In a pure Browser application you could simply use the localStorage. However, since Reach also runs in native Mobile Apps and Mobile OSs sometimes periodically clear the data in the localStorage you may want to use something a little more persistent. That's where the usePluginDeviceStore hook comes in handy.

note

The hook usePluginStorage has been renamed and deprecated. We encourage you to use usePluginDeviceStore from now on.

import { FC, useEffect, useState } from 'react';
import { usePluginDeviceStore } from '@reach/core';
export const MyComponent: FC = () => {
const { getString, setString } = usePluginDeviceStore();
const [myStorageData, setMyStorageData] = useState<string>();
useEffect(() => {
getString('my-key').then(setMyStorageData);
}, []);
return (
<div>
Storage Data: {myStorageData}
<button onClick={() => setString('my-key', new Date().toISOString())}>
Save me
</button>
</div>
);
};

The Local Storage API offers the following methods:

export interface PluginStorage {
getAllKeys(): Promise<string[]>;
getString(key: string): Promise<string>;
setString(key: string, value: string): Promise<void>;
remove(key: string): Promise<void>;
clearAll(): Promise<void>;
}

Note that we automatically prefix all your keys already - so you don't have to be afraid that someone accidentally overwrites your key or vice versa.

Remote Storage#

Alternatively we also provide remote storage capabilities, these can be accessed through the Reach API. These capabilities should be used, if you want to store your data more permanently and not just as a cache on the user's device. You can share your plugin data across Reach either per user or per subscription.

You may wish to have this value available as soon as possible or even render different components based on it's value, while it's fetching or when it failed to fetch. We have provided two hooks to manage this functionality: usePluginUserStoreValue for a specific user and usePluginSubscriptionStoreValue for all users with access to your plugin in the current subscription. Each hook allows you to get, set, re-fetch, store or delete a value.

note

At the moment we only implemented the following security restrictions concerning these routes in the API:

  • only values from your own subscription can be accessed (usePluginSubscriptionStoreValue & usePluginSubscriptionStore)
  • only values from your own user in your subscription can be accessed (usePluginUserStoreValue & usePluginUserStore)

That means that in theory other plugins can also access your plugin values (by calling the API directly). Additionally it's also possible to make custom API requests to the used routes and access the data of the plugin. Therefore you should not store security relevant data.

Let's take a look at an example on how to track and store a user's last visit of the plugin page:

import { FC } from 'react';
import { PluginProps, usePluginUserStoreValue } from '@reach/core';
export const LastVisitPlugin: FC<PluginProps> = () => {
const {
data: lastVisitedTime,
set: setLastVisitedTime,
isLoading,
error,
refresh,
} = usePluginUserStoreValue<string>('lastVisitedTime');
useEffect(() => {
return () => setLastVisitedTime(new Date().toISOString());
}, []);
return (
<Page title="Your last visit">
Your last visit was on: {getLastVisitedTime()}
<br />
<button onClick={refresh}>Refresh!</button>
</Page>
);
function getLastVisitedTime() {
if (isLoading) {
return 'loading...';
}
if (error || !lastVisitedTime) {
return 'never';
}
return lastVisitedTime;
}
};

We also get access to two additional variables: isLoading and error. They can provide valuable information about the current state of the request. It may happen that our data is "out of date" and we wish to re-fetch it. For this purpose we can use the refresh function that the hook provides.

In case you don't need the value right away, but instead fetch it on-demand, triggered by an event or other means we also provide hooks for advanced scenarios. By using these hooks you are in charge of storing in state the returned value, managing errors and handling initial loading scenarios.

The Remote Storage API offers the following methods:

export interface PluginStore {
get<T>(key: string): Promise<T>;
set<T>(key: string, value: T): Promise<unknown>;
delete(key: string): Promise<unknown>;
}

Let's revisit the last example and use the newly introduced hooks:

import { FC, useState, useEffect } from 'react';
import { PluginProps, usePluginUserStore } from '@reach/core';
const key = 'lastVisitedTime';
export const LastVisitPlugin: FC<PluginProps> = () => {
const [lastVisitedTime, setLastVisitedTime] = useState('Never');
const {
set: setPluginUserValue,
get: getPluginUserValue,
delete: deletePluginUserValue,
} = usePluginUserStore();
useEffect(() => {
// There is no auto fetching on initial load
// we have to manually handle it
fetchLastVisitedTime();
return () => setPluginUserValue(key, new Date().toISOString());
}, []);
return (
<Page title="Your last visit">
Your last visit was on: {lastVisitedTime}
<br />
<button onClick={fetchLastVisitedTime}>Refresh</button>
<button onClick={() => deletePluginUserValue(key)}>Clear</button>
</Page>
);
async function fetchLastVisitedTime() {
try {
const res = await getPluginUserValue(key);
setLastVisitedTime(res || 'Never');
} catch (err) {
console.error(err);
setLastVisitedTime('Never');
}
}
};

Please note that we recommend the usage of the usePluginUserStoreValue and usePluginSubscriptionStoreValue hooks instead.