// @flow import type { Persistor, PersistConfig, PersistorOptions, PersistorState, MigrationManifest, RehydrateAction, RehydrateErrorType, } from './types' import { createStore } from 'redux' import { FLUSH, PAUSE, PERSIST, PURGE, REGISTER, REHYDRATE } from './constants' type PendingRehydrate = [Object, RehydrateErrorType, PersistConfig] type Persist = (PersistConfig, MigrationManifest) => R => R type CreatePersistor = Object => void type BoostrappedCb = () => any const initialState: PersistorState = { registry: [], bootstrapped: false, } const persistorReducer = (state = initialState, action) => { switch (action.type) { case REGISTER: return { ...state, registry: [...state.registry, action.key] } case REHYDRATE: let firstIndex = state.registry.indexOf(action.key) let registry = [...state.registry] registry.splice(firstIndex, 1) return { ...state, registry, bootstrapped: registry.length === 0 } default: return state } } export default function persistStore( store: Object, options?: ?PersistorOptions, cb?: BoostrappedCb ): Persistor { // help catch incorrect usage of passing PersistConfig in as PersistorOptions if (process.env.NODE_ENV !== 'production') { let optionsToTest: Object = options || {} let bannedKeys = [ 'blacklist', 'whitelist', 'transforms', 'storage', 'keyPrefix', 'migrate', ] bannedKeys.forEach(k => { if (!!optionsToTest[k]) console.error( `redux-persist: invalid option passed to persistStore: "${k}". You may be incorrectly passing persistConfig into persistStore, whereas it should be passed into persistReducer.` ) }) } let boostrappedCb = cb || false let _pStore = createStore( persistorReducer, initialState, options && options.enhancer ? options.enhancer : undefined ) let register = (key: string) => { _pStore.dispatch({ type: REGISTER, key, }) } let rehydrate = (key: string, payload: Object, err: any) => { let rehydrateAction = { type: REHYDRATE, payload, err, key, } // dispatch to `store` to rehydrate and `persistor` to track result store.dispatch(rehydrateAction) _pStore.dispatch(rehydrateAction) if (boostrappedCb && persistor.getState().bootstrapped) { boostrappedCb() boostrappedCb = false } } let persistor: Persistor = { ..._pStore, purge: () => { let results = [] store.dispatch({ type: PURGE, result: purgeResult => { results.push(purgeResult) }, }) return Promise.all(results) }, flush: () => { let results = [] store.dispatch({ type: FLUSH, result: flushResult => { results.push(flushResult) }, }) return Promise.all(results) }, pause: () => { store.dispatch({ type: PAUSE, }) }, persist: () => { store.dispatch({ type: PERSIST, register, rehydrate }) }, } if (!(options && options.manualPersist)){ persistor.persist() } return persistor }