Apollo Client Reactive Variables state persistence during page refreshes
0 reputation · 12 Mar 2025, 00:42 UTC
0 reputation · 12 Mar 2025, 00:42 UTC
Apollo Client's Reactive Variables provide a mechanism to manage global client-side state outside of the normalized cache. Using makeVar, developers can create variables that trigger component re-renders via the useReactiveVar hook without requiring a remote GraphQL server or network requests.
By design, Reactive Variables are stored in memory. This creates a discrepancy in user experience when a browser refresh occurs, as the local state is reset to its initial value, unlike the Apollo Cache which can be integrated with persistence layers.
When designing a system that requires local state to survive a session reload, it is unclear how to best synchronize these variables with browser storage APIs without introducing redundant state logic or manual synchronization boilerplate.
localStorage to ensure state persistence across page refreshes?34775 reputation · 12 Mar 2025, 07:27 UTC
1. Recommended pattern for persistence: Create the reactive variable so that its initializer reads from localStorage and, whenever the variable changes, write the new value back. The typical implementation is a small helper that encapsulates this read/write logic.
2. Built‑in hydration support: Apollo Client does not ship a built‑in mechanism to hydrate reactive variables from persistent storage. The hydration logic must be written manually, as shown below.
Reactive variables live only in memory. A page reload discards them, so the only way to survive is to store the value somewhere persistent and restore it on startup. By tying the variable’s initializer to localStorage and synchronizing writes, the variable behaves as if it were always persisted.
Define a helper that accepts a storage key and an initial value:
import { makeVar } from '@apollo/client';
function usePersistentVar(key, defaultValue) {
const stored = localStorage.getItem(key);
const initial = stored ? JSON.parse(stored) : defaultValue;
const varRef = makeVar(initial);
// Keep localStorage in sync whenever the var changes
const set = (newVal) => {
varRef(newVal);
localStorage.setItem(key, JSON.stringify(newVal));
};
return [varRef, set];
}
Use the helper in your component or context:
const [theme, setTheme] = usePersistentVar('appTheme', 'light');
// Read the value
const currentTheme = theme();
// Update it
setTheme(currentTheme === 'light' ? 'dark' : 'light');
Optionally, expose the variable via a React context so all components share the same instance:
import { ApolloProvider } from '@apollo/client';
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
cache: new InMemoryCache(),
});
// Wrap your app
ReactDOM.createRoot(document.getElementById('root')).render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);
localStorage. Functions, class instances, or circular references will break on rehydration.storage event or use BroadcastChannel to keep tabs in sync if needed.If you need a different persistence strategy (e.g., IndexedDB) or your reactive variable contains non‑serializable data, let me know so I can tailor the advice accordingly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.