Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whitelist reducer subproperty only? #277

Closed
dwilt opened this issue Feb 12, 2017 · 10 comments
Closed

Whitelist reducer subproperty only? #277

dwilt opened this issue Feb 12, 2017 · 10 comments

Comments

@dwilt
Copy link

dwilt commented Feb 12, 2017

If I have an object on my state called purchases and it has two properties, an array called 'list' and a boolean called 'syncing':

{
  list: [],
  syncing: false
}

Is there a way I can just whitelist my list array like so?

persistStore(store, {
    storage: AsyncStorage,
    whitelist: [
        `purchases.list`
    ]
});

It doesn't seem to work.

@dwilt dwilt changed the title Whitelist subproperty? Whitelist reducer subproperty only? Feb 12, 2017
@rt2zz
Copy link
Owner

rt2zz commented Feb 12, 2017

right now to achieve said functionality you would need to implement a custom transform, or use a package like https://github.com/edy/redux-persist-transform-filter

The reason for this is we want to keep redux-persist as simple as possible and preserve our ability to do type checking with typescript and flow.

I think more exploration needs to be done to figure out how to flexibly enable granular dynamic filtration in a performant way. e.g. omit is a costly operation, especially when applied to dynamic content.

If you figure out a good solution please let us know. A few possible solutions without creating a new module:

  • move loading states into its own reducer which is not persisted. This sounds weird at first but can actually work really well, especially for dynamic content. reselect should make it easy and efficient to recombine the loading state with its content.
  • write a custom REHYDRATE action handler in your reducer that only picks the state you want to persist.

@dwilt
Copy link
Author

dwilt commented Feb 14, 2017

@rt2zz thanks for the feedback. I ended up using the redux-persist-transform-filter and followed the advice that the creator gave and ended up with this:

const persitingReducers = createFilter(
    `purchases.list`
);

persistStore(store, {
    storage: AsyncStorage,
    transforms: [
        persitingReducers
    ]
});

@dwilt dwilt closed this as completed Feb 14, 2017
@icesyc
Copy link

icesyc commented Aug 24, 2017

@dwilt it does not work for me.

@dwilt
Copy link
Author

dwilt commented Aug 24, 2017

@icesyc

move loading states into its own reducer which is not persisted. This sounds weird at first but can actually work really well, especially for dynamic content. reselect should make it easy and efficient to recombine the loading state with its content.

This is what i've been doing and it's been working really well for me in my projects. It keeps the UI state separate from the business data and allows for a better organization

@elquimista
Copy link

@dwilt @icesyc
With redux-persist v5, transforms should be passed to persistConfig parameter of either persistCombineReducers or persistReducer. Like this:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { persistStore, persistCombineReducers } from 'redux-persist';
import { createWhitelistFilter } from 'redux-persist-transform-filter';
import { PersistGate } from 'redux-persist/es/integration/react';
import storage from 'redux-persist/es/storage';
import reducers from './reducers';
import Router from './Router';

const persistConfig = {
  storage,
  key: 'root',
  transforms: [
    createWhitelistFilter('router', []),
    createWhitelistFilter('form', [])
  ]
};
const reducer = persistCombineReducers(persistConfig, reducers);
const store = createStore(reducer);
const persistor = persistStore(store);

const App = () => (
  <Provider store={store}>
    <PersistGate persistor={persistor}>
      <Router />
    </PersistGate>
  </Provider>
);

ReactDOM.render(<App />, document.getElementById('root'));

@Venryx
Copy link

Venryx commented Mar 11, 2019

Just wanted to mention an alternative to redux-persist-transform-filter that I use, for nested blacklisting: #134 (comment)

@mividtim
Copy link

mividtim commented Apr 2, 2019

@dwilt @rt2zz

I came up with a 9-line solution that uses redux-persist without any other dependencies. Create a file persist.js with the following contents:

import { AsyncStorage } from 'react-native';
import { persistReducer } from 'redux-persist';
export function persist(key, whitelist, reducer) {
    return persistReducer({
        key,
        storage: AsyncStorage,
        whitelist,
    }, reducer);
}

You can now whitelist specific fields on any reducer. When creating reducers, you can export the reducer as usual if it doesn't need to persist any keys, but you can use the function defined above to declare which fields to persist, and specify a unique key for the reducer in AsyncStorage. For instance, you can persist entire reducers in your root reducer, e.g.:

import { combineReducers } from 'redux';
// Import your persist function from wherever you created it
import { persist } from 'path/to/persist';
// import your reducers, and define your root reducer as usual,
// e.g. in a const named rootReducer
const rootReducer = combineReducers({
    activeUser,
    configuration,
    credentials,
    someOtherReducer,
});
// Use your imported `persist` function, specify the key "root", and whitelist any
// entire reducers as desired (here, we're not persisting someOtherReducer)
export default persist(
    'root',
    [
        'activeUser',
        'configuration',
        'credentials',
    ],
    rootReducer,
);

But you can also specify only specific keys at reducers at any depth:

// Import your persist function from wherever you created it
import { persist } from 'path/to/persist';
// define your reducer as usual
const initialState = {
    keyToIgnore: 'Don\'t persist this',
    keyToPersist: 'Persist this',
};
function someOtherReducer(state = initialState, action) {
    switch (action.type) {
        // etc.
    }
}
// Then pass it to your `persist` function before exporting,
// with the key name and whitelist of specific keys to persist
export default persist(
    'someOtherReducer',
    ['keyToPersist'],
    someOtherReducer,
);

I hope this helps anyone researching this particular issue. I have implemented this with Flow typings in practice. If anyone is interested in that, feel free to reach out.

@hrachocode
Copy link

For ones who are still searching for updates and appeared here. persist updated his documentation and set up nested configs and combining so you can create multiple configs related to multiple reducers and deep black-white listing
https://github.com/rt2zz/redux-persist#nested-persists

@valeriik
Copy link

valeriik commented Sep 11, 2020

@hrachocode could you please tell how migrate such nested configs with deep black-white listing in case if nested reducers must operate data from parent reducers?

@PiotrKujawa
Copy link

PiotrKujawa commented Mar 27, 2022

Answering the main question - that's possible!

whitelist: [
   'purchases.list'
 ]

and here is what you can do using my package redux-deep-persist, which can be helpful in creating a redux-persist config.

import { getPersistConfig } from 'redux-deep-persist';

const config = getPersistConfig({
    key: 'root',
    storage: LocalStorage, // whatever storage you use
    whitelist: ['purchases.list'], // you can go as deep as you need, using dot notation
    rootReducer, // your root reducer must be also passed here
    ... // any other props from original redux-persist config, omitting the stateReconciler
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants