rt2zz / redux-persist

persist and rehydrate a redux store
MIT License
12.94k stars 866 forks source link

[v5] PersistGate never renders because rehydrate never gets called #603

Open millermatt opened 6 years ago

millermatt commented 6 years ago

I'm trying redux-persist for the first time, but can't get my normal render through PersistGate.

function createPersistentStore ( persistenceKey ) {
    const persistConfig = {
        key: persistenceKey,
        storage,
    };

    /***** APP REDUCER *****/
    const appReducer = persistCombineReducers( persistConfig, reducerConfig );

    /***** STORE *****/
    const store = createStore( appReducer, undefined, applyMiddleware( thunk ) );
    const persistor = persistStore( store );

    return {
        persistor,
        store
    };
}

const { store, persistor } = createPersistentStore( 'foo' );

function onBeforeLift () {
    // take some action before the gate lifts
}

export default class App extends React.Component {
    render() {
        return (
            <Provider store={ store } >
                <PersistGate
                    loading={ null }
                    onBeforeLift={ onBeforeLift }
                    persistor={ persistor }>
                    <Loaded />
                </PersistGate>
            </Provider>
        );
    }
}

const Loaded = () => {
    console.log( 'loaded' );
    return ( <View><Text>loaded</Text></View> );
};

The page is blank and loaded is never logged.

When this code runs in persistStore.js, it dispatches to reducers that are props of the reducerConfig that I passed to persistCombineReducers(), but rehydrate() is never called:

  persistor.persist = function () {
    store.dispatch({ type: _constants.PERSIST, register: register, rehydrate: rehydrate });
  };

Is there more I have to do that is not documented in the readme? Do I need to handle the PERSIST action myself and call rehydrate myself?

Thanks, Matt

rt2zz commented 6 years ago

REHYDRATE will be called by the persisted reducer after it gets state.

Some things to help debug:

  1. you can add debug: true to persist config for extra logging
  2. put a breakpoint in the persistReducer.js handling of PERSIST action, make sure it is coming and and that getStoredState is being called.
henrytao-me commented 6 years ago

I have the same problem. Can you look into it @rt2zz ?

henrytao-me commented 6 years ago

I found the issue. AsyncStorage.getItem was broken on react-native@0.50.x https://github.com/facebook/react-native/issues/14101. Upgrading to react-native@0.51.x fixed this issue. @millermatt

rystraum commented 6 years ago

Just wanted to add here that upgrading to react-native@0.51.x isn't an option for those using expo at the moment. Latest expo version available on the app store only supports 0.50.4.

giacomocerquone commented 6 years ago

I've updated react native but still got the same problem! It works only if I call persistor.purge() on didmount, anyone knows why? Onbeforelift is working too and attaching a reducer to "REHYDRATE" works

henrytao-me commented 6 years ago

Sometime, I do get this issue, rehydrated === false. I have to kill the app and restart. It is quite annoying. It is good to know that persistor.purge will work.

giacomocerquone commented 6 years ago

Actually I have even a stranger behaviour. If I don't use persistor.purge() the component inside PersistGate call an action creator but then this component will not update e componentwillreceiveprops is not called, if I put that persistor.purge (or completely remove persistgate) it works... guys I'm going nuts with this, what could it be? @rt2zz

rt2zz commented 6 years ago

I am not clear what exactly the issue is can you restate the entirety of the problem?

@henrytao-me you are saying you see rehydrated === false even after rehydration completes ocassionally?

nenti commented 6 years ago

This is a really ugly issue that I had in v4 already and now I upgraded to v5 and it is still the case. Easy way to reproduce:

  1. Put a debugger breakpoint in your main App.js render function.
  2. Reload after PERSIST and before REHYDRATE action
  3. REHYDRATE wille never be called again until you kill the app an restart it
henrytao-me commented 6 years ago

As I know, this is not a problem from redux-persist. AsyncStorage.getItem does not trigger some time during on reload that cause redux-persist does not rehydrated. The only way to fix it is killing the app.

nenti commented 6 years ago

Thank you for the info. I will try dropping Redux persist and get my items directly.

  1. Is there a related issue open in react-native?
  2. Is there a way to handle missing response programmatically? Or another storage I can use?
  3. Does this happen in production aswell?

This bug makes usage a pain, especially when you use react - native auto reload function.

henrytao-me commented 6 years ago

@nenti There is an issue here for your reference. https://github.com/facebook/react-native/issues/14101

I haven't encounter this issue (at least as often as I notice) since I use this

    "react": "16.0.0",
    "react-native": "0.51.0",
    "react-redux": "5.0.6",
nenti commented 6 years ago

@henrytao-me I have almost the same versioning as you. From my research it is an android issue with debugger connected and for hot- and live-reloading.

  "react-native": "0.52.1",
  "react-redux": "5.0.6",

It is totally annoying because ofc when you develop debugger is connected and you use reload functions. And the worst part is when you work in a team and your team does not know about this issue.

costagolub commented 6 years ago

In my case updating to the latest versions didn't help me. The app stops working at some moment, rehydrade/persist action never calls, nothing helps except killing the emulator and re-start the react-native. Is it possible to call rehydrate action without PersistGate? Or any other solutions would be good. Thanks!

rt2zz commented 6 years ago

So given the issue is in the underlying storage layer, I think the only remedy redux-persist can offer is a timeout config that will set a max time for rehydration before giving up.

Also I would recommend considering an alternative storage adapter, e.g. https://github.com/robwalkerco/redux-persist-filesystem-storage

rt2zz commented 6 years ago

FYI we are currently contemplating adding timeout in this PR #702

That would at least give users some recourse (without the need to switch storage adapters)

ali-sao commented 6 years ago

Try to define your persistor inside component constructor :

import store from './store';

class App extends Component {
  constructor() {
    super();
    this.persistor = persistStore(store);
  }
  render() {

    return (
      <Provider store={store}>
        <PersistGate persistor={this.persistor} loading={null}>
          <RootRouter />
        </PersistGate>
      </Provider>
    )
  }
}
joshimbriani commented 6 years ago

Switching the default redux-persist store to redux-persist-filesystem-storage fixed this issue for me!

Clementol commented 4 years ago

Try to define your persistor inside component constructor :

import store from './store';

class App extends Component {
  constructor() {
    super();
    this.persistor = persistStore(store);
  }
  render() {

    return (
      <Provider store={store}>
        <PersistGate persistor={this.persistor} loading={null}>
          <RootRouter />
        </PersistGate>
      </Provider>
    )
  }
}

@ali-sao you saved with your codes. thanks