React Store Adapter

Storagefy's React adapter provides seamless persistence for your React state management solutions. Compatible with popular libraries like Redux, Zustand, Jotai, and custom stores, this adapter synchronizes your application state with your chosen storage backend across page reloads and browser sessions.

1. Basic Usage

The React adapter works with various React state management solutions, making it easy to persist your application state:

        
import { startStoragefy, getReactAdapter } from "storagefy";

// Initialize the storage backend
startStoragefy({
  dbName: "my-react-app",
  adapter: "indexedDB", // or "localStorage" or "sessionStorage"
  encrypt: true,
  channelName: "react-sync" // for cross-tab synchronization
});

// Get the React adapter
const { setInStorage, getFromStorage } = getReactAdapter();

// Connect with your store (works with Redux, Zustand, and more)
await getFromStorage(store, "app-state"); // Always call this before setInStorage
await setInStorage(store, "app-state", {
  timeout: 86400000, // 24 hour expiration
  ignoreKeys: ["temporaryData", "loadingStates"],
  syncTabs: true
});
        
      

2. Using the Helper Function

For a more concise approach, use the setReactStorage helper function:

        
import { startStoragefy, setReactStorage } from "storagefy";

// Initialize storage backend
startStoragefy({
  dbName: "my-react-app",
  adapter: "localStorage"
});

// Use the helper function to simplify the process
await setReactStorage(store, "app-state", {
  timeout: 3600000, // 1 hour
  ignoreKeys: ["_temp", "loading"],
  syncTabs: true
});
        
      

3. Test It

3.1 Redux

3.2 Zustand

3.3 Jotai

4. Advanced Configuration

You can directly instantiate the ReactAdapter class for more advanced control:

        
import { ReactAdapter, IndexedDBAdapter } from "storagefy";

// Create storage adapter instance
const storageAdapter = new IndexedDBAdapter({ 
  dbName: "react-app",
  encrypt: true
});

// Create React adapter with the storage adapter
const reactAdapter = new ReactAdapter(storageAdapter);

// Connect with your store
await reactAdapter.getFromStorage(store, "user-settings");
await reactAdapter.setInStorage(store, "user-settings", {
  ignoreKeys: ["authToken"],
  timeout: 604800000 // 1 week
});
        
      

5. Supported State Management Libraries

The React adapter is designed to work with multiple state management solutions:

Redux

Works with standard Redux stores by utilizing getState, dispatch, and subscribe methods.

          
import { createStore } from 'redux';
import { startStoragefy, setReactStorage } from "storagefy";

const reducer = (state = { count: 0 }, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    case 'SET_STATE_FROM_STORAGE': // Important: handle this action type
      return { ...state, ...action.payload };
    default:
      return state;
  }
};

const store = createStore(reducer);

// Persist Redux store
await setReactStorage(store, "redux-state");
          
        

Note: Your Redux reducer should handle the SET_STATE_FROM_STORAGE action type for proper hydration from persistent storage.

Zustand

Compatible with Zustand stores using getState, setState, and subscribe methods.

          
import create from 'zustand';
import { startStoragefy, setReactStorage } from "storagefy";

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  reset: () => set({ count: 0 })
}));

// Persist Zustand store
await setReactStorage(useStore, "zustand-state");
          
        

Jotai

Works with Jotai atom values using get, set, and subscription mechanisms.

          
import { atom, useAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import { startStoragefy, getReactAdapter } from "storagefy";

const countAtom = atom(0);

// For Jotai, you'll need a store-like wrapper
const jotaiStore = {
  get: () => countAtom.init,
  set: (value) => {
    countAtom.init = value;
    // Trigger updates to subscribers
  },
  subscribe: (callback) => {
    // Setup subscription to atom changes
    // Return unsubscribe function
  }
};

// Get the adapter
const { setInStorage, getFromStorage } = getReactAdapter();

// Persist Jotai state
await getFromStorage(jotaiStore, "jotai-state");
await setInStorage(jotaiStore, "jotai-state");
          
        

Custom React State Hooks

You can adapt custom React hooks or state management solutions by creating a compatible store interface.

          
import { useState, useEffect } from 'react';
import { startStoragefy, setReactStorage } from "storagefy";

// Create a store-like interface for your custom hook
function createStore(initialState) {
  let state = initialState;
  const listeners = new Set();
  
  return {
    getState: () => state,
    setState: (newState) => {
      state = typeof newState === 'function' ? newState(state) : newState;
      listeners.forEach(listener => listener(state));
    },
    subscribe: (listener) => {
      listeners.add(listener);
      return () => listeners.delete(listener);
    }
  };
}

const customStore = createStore({ theme: 'light', language: 'en' });

// Persist custom store
await setReactStorage(customStore, "custom-state");
          
        

6. Configuration Options

The setInStorage method accepts the following options:

Options Object

  • ignoreKeys: Array of store property keys to exclude from persistence

    Use this for sensitive data, temporary state, or large objects that shouldn't be persisted.

  • timeout: Expiration duration in milliseconds

    After this duration, the stored data will be considered expired and removed.

  • syncTabs: Boolean flag to enable cross-tab synchronization

    When true, changes made in one browser tab will be reflected in all others. Requires channelName to be set during startStoragefy.

7. Cross-Tab Synchronization

The React adapter supports real-time synchronization of state across multiple browser tabs:

        
// Initialize with channel name for cross-tab communication
startStoragefy({
  dbName: "my-react-app",
  adapter: "indexedDB",
  channelName: "react-sync" // Required for cross-tab sync
});

// Enable syncTabs in your store configuration
await setReactStorage(store, "shared-state", {
  syncTabs: true
});

// Now changes to this store in one tab will be reflected in other tabs
        
      

The synchronization uses the BroadcastChannel API under the hood and intelligently handles different store types to apply updates appropriately.

8. API Reference

getReactAdapter([options])

Returns a configured React adapter instance. Options include:

  • adapterParams: Configuration for the underlying storage adapter

Returns: { setInStorage, getFromStorage } methods

setReactStorage(store, key, [options])

Helper function that internally calls getFromStorage followed by setInStorage.

  • store: React state store instance (Redux, Zustand, etc.)
  • key: Storage key string
  • options: Configuration options (ignoreKeys, timeout, syncTabs)

Returns: Promise resolving to true on success

ReactAdapter Class Methods

  • setInStorage(store, key, options): Persists store state and sets up subscription
  • getFromStorage(store, key): Loads persisted state into store
  • destroy(): Cleans up subscriptions when adapter is no longer needed

9. Best Practices

  • Always call getFromStorage before setInStorage to hydrate your store with existing data
  • For Redux stores, ensure your reducer handles the SET_STATE_FROM_STORAGE action type
  • Use ignoreKeys for sensitive information, temporary UI state, or large objects
  • Set appropriate timeout values based on the nature of your data
  • When using syncTabs, be mindful of complex state mutations that could conflict across tabs
  • Call destroy() on the adapter instance when unmounting components to avoid memory leaks
  • Consider using separate storage keys for different logical parts of your application state