Redux-persist: How to wait until rehydration in background jobs

Created on 16 Nov 2017  路  6Comments  路  Source: rt2zz/redux-persist

Hi,

I'm currently working on a react-native app where data is always valid for 2 weeks. Therefore I use apollo-client and redux-persist to cache those data to the file system. Now I'm trying to register a background job with react-native-background-job to delete old data.

My store setup looks like this:

import ApolloClient, { createNetworkInterface } from 'apollo-client'
import { applyMiddleware, combineReducers, createStore } from 'redux'

import createSagaMiddleware from 'redux-saga'
import logger from 'redux-logger'
import rootReducer from '../reducers'
import { persistReducer, persistStore } from 'redux-persist'
import rootSaga from '../sagas/root'
import FSStorage, { CacheDir } from 'redux-persist-fs-storage'
import { composeWithDevTools } from 'remote-redux-devtools'

export default (initialState = {}) => {
    const sagaMiddleware = createSagaMiddleware()
    const client = new ApolloClient({
        networkInterface: createNetworkInterface({
            uri: 'https://804rljq7q.lp.gql.zone/graphql'
        })
    })
    const apolloPersistConfig = {
        key: 'apollo',
        storage: FSStorage(CacheDir, 'MensaApp')
    }
    const enhancer = composeWithDevTools(
        applyMiddleware(logger, sagaMiddleware, client.middleware())
    )
    const store = createStore(
        combineReducers({
            root: rootReducer,
            apollo: persistReducer(apolloPersistConfig, client.reducer())
            // apollo: client.reducer()
        }),
        initialState,
        enhancer
    )
    const persistor = persistStore(store)

    sagaMiddleware.run(rootSaga)

    return { store, client, persistor }
}

In my Background Job I have to configure the store and then wait until rehydration somehow. This is the point where I'm stuck.

BackgroundJob.register({
    jobKey: TIDY_UP_CACHE_JOB,
    job: () => {
        const { store, client, persistor } = configureStore()

        // wait until rehydration, but how?

        client.resetStore()
    }
})

If you have any hint for me on how to register some kind of callback that gets executed after rehydration is done, that'll be awesome.

Cheers,
Martin

Most helpful comment

two entry points I can think of:

  1. callback on persistStore:
let persistor = persistStore(store, null, () => {
  // this will be invoked after rehydration is complete
}
  1. onBeforeLift of PersistGate
<PersistGate 
  onBeforeLift={this.doSomething}

All 6 comments

two entry points I can think of:

  1. callback on persistStore:
let persistor = persistStore(store, null, () => {
  // this will be invoked after rehydration is complete
}
  1. onBeforeLift of PersistGate
<PersistGate 
  onBeforeLift={this.doSomething}

When you setup your store you can do the following:

let rehydrationComplete
let rehydrationFailed

const rehydrationPromise = new Promise((resolve, reject) => {
  rehydrationComplete = resolve
  rehydrationFailed = reject
})

export function rehydration () {
  return rehydrationPromise
}

export default (initialState = {}) => {
  // ... setup middleware and all the other boiler plate
  const persistor = persistStore(store)
  sagaMiddleware.run(rootSaga)

  rehydrationComplete() // if you detect that an error occurred during rehydration you can call rehydrationFailed
  return { store, client, persistor }
}

You're exporting a function that returns a promise that resolves when the store is rehydrated.
You can then use it anywhere, incl. in background jobs, like this:

import {rehydration} from '...'

await rehydration() // this will continue when rehydration is complete. If the store is already rehydrated, it will continue immediately because the promise is already resolved. If rehydration failed, it will throw an exception

using @rt2zz's 1. method, I got an error:

TypeError: Cannot read property 'skipRestore' of null

// defaults
  13 | // @TODO remove shouldRestore
> 14 | var shouldRestore = !config.skipRestore;
  15 | if (process.env.NODE_ENV !== 'production' && config.skipRestore) console.warn('redux-persist: config.skipRestore has been deprecated. If you want to skip restoration use `createPersistor` instead');
  16 | 
  17 | var purgeKeys = null;

Anyone has a clue what to put instead of null then?

I'm currently just doing the following in store.js:

const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer, composeEnhancers(applyMiddleware(thunk)));

export default (cb) => {
  const persistor = persistStore(store, null, cb);
  return { store, persistor };
};

But I run into an issue where the store in the background isn't persisted to the foreground...

One thing that seems to work to me is a combination of @daramasala and @rt2zz codes:

store.js

let rehydrationComplete;
let rehydrationFailed;

const rehydrationPromise = new Promise((resolve, reject) => {
  rehydrationComplete = resolve;
  rehydrationFailed = reject;
});

export function rehydration () {
  return rehydrationPromise;
};

export default (initialState = {}) => {
  // ... setup middleware and all the other boiler plate
  const persistor = persistStore(store, null, () => {
    // this will be invoked after rehydration is complete
    rehydrationComplete();
  });
  return { store, client, persistor }
}

Then:

import { rehydration } from './store'

await rehydration();

How can I check by the time I am using store.getState(), the rehydration process has already been done? Because I am using store.getState() somewhere soon and it does not give me the persisted value. Instead, it gives me the default value that I specified in the reducer.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

HeratPatel1 picture HeratPatel1  路  3Comments

elado picture elado  路  4Comments

thenewt15 picture thenewt15  路  3Comments

fredp96 picture fredp96  路  4Comments

jamesone picture jamesone  路  4Comments