Open dbartholomae opened 5 years ago
We could look and see how redux-persist works and perhaps create a new extension for it
I can investigate this more this week
Great! If there is any way I can help, please let me know. In the mean time I will be working on a plugin for redux beacon
Hey there, I happened to do this exact thing recently (using redux persist along side redux dynamic module) and here's how I did it
have a normal config for your persist:
import { persistReducer } from "redux-persist";
const persistConfig = {key: "PersistKey", storage, whitelist, blacklist};
const persistedReducer = persistReducer(config, yourReducer)
And then create a module for your use with redux dynamic module:
export function getYourModule() {
id: "YourID",
reducerMap: {
reducer: persistedReducer
}
}
Now the main problem with redux dynamic module is that your module isnt loaded until it being render, so with all we did so far at the start of your app the persistedReducer didnt get put into store yet, so you need to call .persist() on the constructor of your component (which got called whenever your component got render and getYourModule get put into the redux dynamic module's store) to tell redux persistor that you have just loaded this new reducer, so now it should try to look for the persist data of the newly loaded reducer.
As for the store, you can do something like this:
import { createStore } from 'redux-dynamic-modules';
import { persistStore } from 'redux-persist';
const store = createStore(.......);
const persistStore = persistStore(store);
Save this persistStore in some way, so on the constructor of your component like I talk about earlier you can call "persistStore.persist();", and it should persist your reducer in that dynamic module now
@MegazeroMZ can you give a better example on how/when/where you are calling
persistStore.persist();
?
So continue with what I already have in the previous post, let's say I have a component called Setting, and I making it a dynamic module using redux-dynamic-module, then I will have this:
<DynamicModuleLoader modules={getYourModule()}>
<Setting />
</DynamicModuleLoader>
Now once this Setting got render on the screen, getYourModule() will finally be put into the store by redux-dynamic-module, so now if you log out the state of your reducer you will finally see the state in the persistedReducer got put into the store. So now is when you can call persistStore.persist(), so it make sense to call it in the constructor of Setting component, so you will have Setting component look something like this:
// import persistStore from where you saved it
class Setting extends React.Component {
constructor(props) {
super(props);
persistStore.persist();
}
... // the rest of the component
@MegazeroMZ thanks, I already figured it out and it works.
Also it is worth mentioning that the PersistGate
component is not working with redux-dynamic-modules
. When I tried using it it get's stuck at the loading phase
@MegazeroMZ thanks, I already figured it out and it works.
Also it is worth mentioning that the
PersistGate
component is not working withredux-dynamic-modules
. When I tried using it it get's stuck at the loading phase
PersistGate does work with redux-dynamic-modules.
This one is slightly more complicated. The role of the PersistGate is to stop the app from being load until the persist has check all the reducers for persisted stuff. BUT, and a big BUT here, if there's no persistedReducer passed to the persistor prop in PersistGate, then PersistGate thinking that it havent check all reducer yet cause it couldnt find any persistedReducer, hence it stuck in loading, or stuck in PersistGate in this case. In case the only persistedReducer you use are inside 1 of the redux-dynamic-module, because it havent been load yet so PersistGate cant find it, hence it stuck.
There's 2 way around this. 1 is you cheated by purposely add a persistedReducer of some unimportant, even unused, reducer to the persistor prop in PersistGate. 2 is you check if there's any persistedReducer in the persistorStore or not, if not then dont use PersistGate, if there is then use PersistGate. You can check this by checking persistStore.getState().registry.length, if it > 0 then you have some persistedReducer in the store, if its 0 then you dont.
Would it be possible to add this implementation to one of the examples here https://github.com/microsoft/redux-dynamic-modules/tree/master/packages?
This is how it worked for us:
const authModule = {
id: 'auth',
reducerMap: {
auth: persistReducer(createPersistConfig('local_storage_auth'), authReducer)
}
}
export const store = createStore({}, authModule)
export const persistor = persistStore(store)
This way each module can decide on its own what to persist, but localStorage will be a bit cluttered
Just use advancedCombineReducers
:
// createStore.ts
import { combineReducers } from 'redux'
import { createStore as createDynamicStore } from 'redux-dynamic-modules'
import { createReducer } from 'reduxsauce' // can use your own reducers as well
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
const reducer = createReducer({}, { _: state => state }) // or write your own initial reducer
const mandatoryInitialModule = {
id: '_initial_module',
reducerMap: { _: reducer }
}
export const createStore = () => {
const store = createDynamicStore(
{
extensions: [...],
advancedCombineReducers: reducers => // combineReducers by default, see https://github.com/microsoft/redux-dynamic-modules/blob/7492972e1d4f969eae70c28ec9fc55f7cbb75e55/packages/redux-dynamic-modules-core/src/Managers/ReducerManager.ts#L52
persistReducer(
{
key: '2019_12_16',
storage,
whitelist: ['user_session'], // or whatever reducer may appear later
},
combineReducers(reducers as any)
) as any,
},
mandatoryInitialModule
)
const persistor = persistStore(store)
return { store, persistor }
}
then:
// App.tsx
import React from 'react'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist'
import { createStore } from './createStore'
const { store, persistor } = createStore()
const App = ({ children }: any) => (
<Provider store={store}><PersistGate persistor={persistor}>{children}</PersistGate></Provider>
)
export default App
As mentioned by @MegazeroMZ, you need that initial mandatory reducer to fake PersistGate default behaviour, otherwise you'll se an infinite loading screen. As for creating an extension for it, dynamic-modules should expose an API for the store, because right after inserting and extension we must call persistStore and save the persistor somewhere.
This is how it worked for us:
const authModule = { id: 'auth', reducerMap: { auth: persistReducer(createPersistConfig('local_storage_auth'), authReducer) } } export const store = createStore({}, authModule) export const persistor = persistStore(store)
This way each module can decide on its own what to persist, but localStorage will be a bit cluttered
@dbartholomae does this means that we export a store from each of the modules? Is there any example implementation using the functional components?
No - the first part is in each module, the second part only in the central store creation. Unfortunately I don't have a public example at the moment.
No - the first part is in each module, the second part only in the central store creation. Unfortunately I don't have a public example at the moment.
Ok no worries, I tried it as you have said and it is working. Thanks.
any update? I also encountered the same problem. After dynamically registering the module, I can no longer write to the local cache.
found a solution
import { persistCombineReducers } from 'redux-persist'
export const advancedCombineReducers = (reducers) => persistCombineReducers(PersistConfig, reducers)
and register a redux-saga watcher: call persist when 'moduleAdd' and 'moduleRemove' action called
function * watchDynamic () {
yield takeEvery(['@@Internal/ModuleManager/ModuleAdded', '@@Internal/ModuleManager/ModuleRemoved'], function * () {
persistor.persist()
})
}
Here is another solution, based on the responses given by @olabala and @leonardofalk:
import { createStore as createDynamicStore } from "redux-dynamic-modules";
...
export const store = createDynamicStore(
{
...,
advancedCombineReducers: (reducers) =>
persistCombineReducers(
{
key: "root",
storage,
whitelist: [],
},
reducers
),
},
initialModuleWithReducer() // <= this is mandatory!
);
export const persistor = persistStore(store);
<Provider>
(generally App
), and use the persistor variable created previously:<Provider store={store}>
<PersistGate persistor={persistor}>
{children}
</PersistGate>
</Provider>
export function moduleConfig() {
return {
id: "the-module-slice",
reducerMap: {
"the-slice": persistReducer(
{
key: "the-slice",
storage,
whitelist: ["draft"] // if you want to save a specific piece of your slice. In this case, draft belongs to "the-slice" part of the store
},
rootReducer
),
},
...
};
}
redux-dynamic-modules
triggers when a module is added/removed. this way, it can call the persist action explicitly:export function* rootSaga() {
yield all([watchDynamicReduxModules()/* ... other sagas.... */]);
}
function* watchDynamicReduxModules() {
yield takeEvery(
[
"@@Internal/ModuleManager/ModuleAdded",
"@@Internal/ModuleManager/ModuleRemoved",
],
function* () {
persistor.persist();
}
);
}
Leave me a reaction/comment to know if the same strategy works for you, so maybe it could be a workaround while the official plugin is released.
Hi there!
I really like this library - and I also really like redux-persist which allows to save parts of the store to local storage. Unfortunately both this module and redux-persist require to use their own method to create a redux store, and these methods are not chainable if I am not mistaken. What would the preferred way to solve this? If there is no way yet but an idea of how to solve this as an additional feature in this library, I would be willing to try myself on a PR. I have also opened an issue with the other repo.
Best, Daniel