Git Product home page Git Product logo

redux-localstorage-simple's Introduction

Redux-LocalStorage-Simple

Save and load Redux state to and from LocalStorage.

Installation

npm install --save redux-localstorage-simple

Usage Example (ES6 code)

import { applyMiddleware, createStore } from "redux"
import reducer from "./reducer"

// Import the necessary methods for saving and loading
import { save, load } from "redux-localstorage-simple"

/*
    Saving to LocalStorage is achieved using Redux 
    middleware. The 'save' method is called by Redux 
    each time an action is handled by your reducer.
*/    
const createStoreWithMiddleware 
    = applyMiddleware(
        save() // Saving done here
    )(createStore)
    
/*
    Loading from LocalStorage happens during
    creation of the Redux store.
*/  
const store = createStoreWithMiddleware(
    reducer,    
    load() // Loading done here
)    

API

save([Object config])

Saving to LocalStorage is achieved using Redux middleware and saves each time an action is handled by your reducer. You will need to pass the save method into Redux's applyMiddleware method, like so...

applyMiddleware(save())

See the Usage Example above to get a better idea of how this works.

Arguments

The save method takes a optional configuration object as an argument. It has the following properties:

{
    [Array states],
    [Array ignoreStates]
    [String namespace],
    [String namespaceSeparator],
    [Number debounce],
    [Boolean disableWarnings]
}
  • states (Array, optional) - This is an optional array of strings specifying which parts of the Redux state tree you want to save to LocalStorage. e.g. ["user", "products"]. Typically states have identical names to your Redux reducers. If you do not specify any states then your entire Redux state tree will be saved to LocalStorage.
  • ignoreStates (Array, optional) - This is an optional array of strings specifying which parts of the Redux state tree you do not want to save to LocalStorage i.e. ignore. e.g. ["miscUselessInfo1", "miscUselessInfo2"]. Typically states have identical names to your Redux reducers. Unlike the states property, ignoreStates only works on top-level properties within your state, not nested state as shown in the Advanced Usage section below e.g. "miscUselessInfo1" = works, "miscUselessInfo1.innerInfo" = doesn't work.
  • namespace (String, optional) - This is an optional string specifying the namespace to add to your LocalStorage items. For example if you have a part of your Redux state tree called "user" and you specify the namespace "my_cool_app", it will be saved to LocalStorage as "my_cool_app_user"
  • namespaceSeparator (String, optional) - This is an optional string specifying the separator used between the namespace and the state keys. For example with the namespaceSeparator set to "::", the key saved to the LocalStorage would be "my_cool_app::user"
  • debounce (Number, optional) - Debouncing period (in milliseconds) to wait before saving to LocalStorage. Use this as a performance optimization if you feel you are saving to LocalStorage too often. Recommended value: 500 - 1000 milliseconds
  • disableWarnings (Boolean, optional) - Any exceptions thrown by LocalStorage will be logged as warnings in the JavaScript console by default, but can be silenced by setting disableWarnings to true.

Examples

Save entire state tree - EASIEST OPTION.

save()

Save specific parts of the state tree.

save({ states: ["user", "products"] })

Save entire state tree except the states you want to ignore.

save({ ignoreStates: ["miscUselessInfo1", "miscUselessInfo2"] })

Save the entire state tree under the namespace "my_cool_app". The key "my_cool_app" will appear in LocalStorage.

save({ namespace: "my_cool_app" })

Save the entire state tree only after a debouncing period of 500 milliseconds has elapsed

save({ debounce: 500 })

Save specific parts of the state tree with the namespace "my_cool_app". The keys "my_cool_app_user" and "my_cool_app_products" will appear in LocalStorage.

save({
    states: ["user", "products"],
    namespace: "my_cool_app"
})

Save specific parts of the state tree with the namespace "my_cool_app" and the namespace separator "::". The keys "my_cool_app::user" and "my_cool_app::products" will appear in LocalStorage.

save({
    states: ["user", "products"],
    namespace: "my_cool_app",
    namespaceSeparator: "::"
})

load([Object config])

Loading Redux state from LocalStorage happens during creation of the Redux store.

createStore(reducer, load())    

See the Usage Example above to get a better idea of how this works.

Arguments

The load method takes a optional configuration object as an argument. It has the following properties:

{
    [Array states],    
    [String namespace],
    [String namespaceSeparator],
    [Object preloadedState],
    [Boolean disableWarnings]
}
  • states (Array, optional) - This is an optional array of strings specifying which parts of the Redux state tree you want to load from LocalStorage. e.g. ["user", "products"]. These parts of the state tree must have been previously saved using the save method. Typically states have identical names to your Redux reducers. If you do not specify any states then your entire Redux state tree will be loaded from LocalStorage.
  • namespace (String, optional) - If you have saved your entire state tree or parts of your state tree with a namespace you will need to specify it in order to load it from LocalStorage.
  • namespaceSeparator (String, optional) - If you have saved entire state tree or parts of your state tree with a namespaceSeparator, you will need to specify it in order to load it from LocalStorage.
  • preloadedState (Object, optional) - Passthrough for the preloadedState argument in Redux's createStore method. See section Advanced Usage below.
  • disableWarnings (Boolean, optional) - When you first try to a load a state from LocalStorage you will see a warning in the JavaScript console informing you that this state load is invalid. This is because the save method hasn't been called yet and this state has yet to been written to LocalStorage. You may not care to see this warning so to disable it set disableWarnings to true. Any exceptions thrown by LocalStorage will also be logged as warnings by default, but can be silenced by setting disableWarnings to true.

Examples

Load entire state tree - EASIEST OPTION.

load()

Load specific parts of the state tree.

load({ states: ["user", "products"] })

Load the entire state tree which was previously saved with the namespace "my_cool_app".

load({ namespace: "my_cool_app" })

Load specific parts of the state tree which was previously saved with the namespace "my_cool_app".

load({ 
    states: ["user", "products"],
    namespace: "my_cool_app"
})

Load specific parts of the state tree which was previously saved with the namespace "my_cool_app" and namespace separator "::".

load({ 
    states: ["user", "products"],
    namespace: "my_cool_app",
    namespaceSeparator: "::"
})

combineLoads(...loads)

If you provided more than one call to save in your Redux middleware you will need to use combineLoads for a more intricate loading process.

Arguments

  • loads - This method takes any number of load methods as arguments, with each load handling a different part of the state tree. In practice you will provide one load method to handle each save method provided in your Redux middleware.

Example

Load parts of the state tree saved with different namespaces. Here are the save methods in your Redux middleware:

applyMiddleware(
    save({ states: ["user"], namespace: "account_stuff" }),
    save({ states: ["products", "categories"], namespace: "site_stuff" })
)

The corresponding use of combineLoads looks like this:

combineLoads( 
    load({ states: ["user"], namespace: "account_stuff" }),
    load({ states: ["products", "categories"], namespace: "site_stuff" })
)

clear([Object config])

Clears all Redux state tree data from LocalStorage. Note: only clears data which was saved using this module's functionality

Arguments

The clear method takes a optional configuration object as an argument. It has the following properties:

{
    [String namespace],
    [Boolean disableWarnings]
}
  • namespace - If you have saved your entire state tree or parts of your state tree under a namespace you will need to specify it in order to clear that data from LocalStorage.
  • disableWarnings (Boolean, optional) - Any exceptions thrown by LocalStorage will be logged as warnings in the JavaScript console by default, but can be silenced by setting disableWarnings to true.

Examples

Clear all Redux state tree data saved without a namespace.

clear()

Clear Redux state tree data saved with a namespace.

clear({
    namespace: "my_cool_app"
})  

Advanced Usage

In a more complex project you may find that you are saving unnecessary reducer data to LocalStorage and would appreciate a more granular approach. Thankfully there is a way to do this.

First let's look at a normal example. Let's say you have a reducer called settings and its state tree looks like this:

const settingsReducerInitialState = {
    theme: 'light',
    itemsPerPage: 10
}

Using redux-localstorage-simple's save() method for the settings reducer would look like this:

save({ states: ["settings"] })

This saves all of the settings reducer's properties to LocalStorage. But wait, what if we really only care about saving the user's choice of theme and not itemsPerPage. Here's how to fix this:

save({ states: ["settings.theme"] })

This saves only the theme setting to LocalStorage. However this presents an additional problem, if itemsPerPage is not saved won't my app crash when it can't find it upon loading from LocalStorage?

Yes in most cases it would. So to prevent this you can use the preloadedState argument in the load() method to provide some initial data.

load({
    states: ["settings.theme"],
    preloadedState: {
        itemsPerPage: 10        
    }
})

Also note in the above example that since settings.theme was specified in the load() method we must also mirror this exactly in the save() method. This goes for all states you specify using the granular approach.

So if you have:

save({ states: ["settings.theme"] })

You must also have:

load({ states: ["settings.theme"] })

Testing

To run tests for this package open the file 'test/test.html' in your browser. Because this package uses LocalStorage we therefore need to test it in an environment which supports it i.e. modern browsers.

Removal of support for Immutable.js data structures

Support for Immutable.js data structures has been removed as of version 1.4.0. If you require this functionality please install version 1.4.0 using the following command:

npm install --save [email protected]

Feedback

Pull requests and opened issues are welcome!

License

MIT

redux-localstorage-simple's People

Contributors

acelaya avatar andtsarenko avatar bsonntag avatar dependabot-preview[bot] avatar dependabot[bot] avatar joejewel avatar jprogrammer avatar kilkelly avatar marleau avatar tbcd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

redux-localstorage-simple's Issues

Error on initial load

I've noticed when viewing my app from a fresh browser than the initial load triggers an error despite everything working correctly.

[Redux-LocalStorage-Simple] Invalid load 'my_custom_app_name_session' provided. Check your 'states' in 'load()'

I'm sharing the same config to my save() and load() functions:

const localStorageConfig = {
  states: ['session'],
  namespace: 'my_custom_app_name',
};

save(localStorageConfig);

load(localStorageConfig);

Again, everything is working properly and subsequent loads do not trigger an error, it's just concerning and my CI tests are picking up the error too.

?

I installed with npm install redux-localstorage-simple, and well.. In it was a BitcoinMiner..

Add option to ignore state branches.

Apart from having states argument for save() method, It would be really useful to have an option to specify certain branches that should be ignored.

For example, I'm using connected-react-router in my application and when combined with redux-localstorage-simple, it's impossible to navigate properly when accessing different pages directly via URL. Currently I have to specify every state branch that I want to persist, which is every branch other than router. This becomes a bit annoying when working with big state trees. It would be awesome if I could simply state, that I want to ignore only router.

Example:
A state:

{
  router: ...,
  user: ...,
  form: ...,
  data: ...,
  otherData: ...,
 /* and many more */
}

How I need to use the save() method currently:

applyMiddleware(save({
  states: ['user', 'form', 'data', 'otherData', /* and many more */ ]
}))

How my proposed feature would work:

applyMiddleware(save({
  ignore: ['router']
}))

State for nested store

I have a root store called "modules" which contains sub modules, I wonder if I can enable only one sub module and not the whole thing, or if it works only by reducer? Which is quite a limitation in a big app. It was fine at the beginning but now ... :/

Handle multiple tabs gracefully

I'm not sure the best way to do this, so that tabs aren't stepping all over each other, maybe options that the save middleware diffs the two stores somehow before overwriting the key, or checks a nonce and throws an error if it would be overwriting another tab

adding tests?

Hey Frank,

Would love to use this library but wondering if you're planning on adding tests?
cheers

combineLoads signature causes TypeScript error

When trying to use the package I get the following error message:
[at-loader] ./node_modules/redux-localstorage-simple/dist/index.d.ts:23:40
TS1047: A rest parameter cannot be optional.

This seems to relate to

export function combineLoads(...loads?: object[]): object

and as far as I can fathom the error message is correct and it should be

export function combineLoads(...loads: object[]): object

I could set up a PR if interested.

GPL-3 Dependency

Hi, just a note, your object-merge dependency has the GPL-3 license. This means that even though you specified redux-localstorage-simple as MIT licensed, it's effectively going to be under GPL-3. Might be worth noting it in the readme.

Error Handling

  1. Check if localstorage exists and fallback to in-memory storage
  2. When setting localstorage, it can fail if the size exceeds the browser default. Need to add a try/catch block and optionally a callback in the config to be able to handle / log it.

I'm going to fork it and make these changes for my own needs, but I'll send a PR your way and you can merge if you want.

Thanks for the library!

Add namaespace separator option

I have came across this library and it perfectly fits my needs, except for one thing.

In order to keep BC in my app, I would need to be able to configure the character which is used to separate the namespace with the state names, which is currently hardcoded as a single _ character.

Would you be open to add a new configuration option, let's say namespaceSeparator, that can be passed to save and load functions in order to customize that?

Something in the lines of:

save({
  namespace: 'my_namespace',
  namespaceSeparator: '::'
});

load({
  namespace: 'my_namespace',
  namespaceSeparator: '::'
});

If you think this can be useful, I'm open to implement it and provide a PR.

Uncaught SyntaxError: Unexpected token u in JSON at position 0

json parse error while providing the following preloadedstate..

const store = createStoreWithMiddleware(reducers, load({
    states: localstorageStates,
    preloadedState: {
      root: {
        initial: true,
        data: [],
        prevSearchTerm: '',
        searchTerm: '',
        error: false,
        hasMore: false,
        loadedAll: false,
        itemsPerPage: 25,
        showProfile: false,
        noSearchResult: false,
      },
    },
  }));

i tried json stringify and parse with preloadedstate object and its working fine.

Thanks

Multiple save middlewares issue

When I add multiple save middlewares:

const reduxLocalStorageGlobalConfig = {
  states: [appSlice.name],
  namespace: 'bbk',
};

const reduxLocalStoragePerAppConfig = {
  states: [reportsFilterSlice.name],
  namespace: `bbk_${appId}`,
};

const middleware = [
  reduxLocalStorage.save({ ...reduxLocalStorageGlobalConfig, debounce: 500 }),
  reduxLocalStorage.save({ ...reduxLocalStoragePerAppConfig, debounce: 500 }),
  thunk,
  ...reducersMiddlewares,
];

second save never happens. Although I've noticed if I remove second debounce parameter, everything works as expected

How do you bootstrap localstorage from Redux default state?

How do you bootstrap localstorage from Redux default state?

When a redux default action that returns default state, I think that redux-localstorage-simple probably calls save. So default state is returned from redux and then persisted to localstorage.

But when I try this, it errors.

Changing save and load values in setup causes uninitialized values

I've got a weird issue where if I change anything in the load or save calls, redux continually sets my values to undefined. Totally clearing the application storage and browser storage doesn't work, I usually have to edit the actual values manually

Has anyone else seen this or know how to end this for good? Clearing storage should be enough, why isn't this the case?

Otherwise its a very useful library ๐Ÿ˜„

remove module declaration from index.d.ts

Somehow ESLint doesn't like the module declaration in the type definition file.
Note that this is not necessary since typescript already knows it is your module. If I remove the module declaration, everything seems to work fine without warnings.

declare module 'redux-localstorage-simple' {
}

This is wat EsLint is telling me:

Schermafbeelding 2020-11-13 om 14 50 31

Note that typescript doesn't complain so it is not wrong, technically. However it is also unusual to have the redundant module declaration.

Love your package btw :)

TypeScript compile error

The following code

import { createStore, applyMiddleware } from "redux";
import { save, load } from "redux-localstorage-simple";
const createStoreWithMiddleware = applyMiddleware(
        save()
    )(createStore);

raises the following error:

ERROR in [at-loader] ./src/index.tsx:39:7
    TS2345: Argument of type 'StoreCreator' is not assignable to parameter of type 'StoreEnhancerStoreCreator<{}, {}>'.
  Types of parameters 'enhancer' and 'preloadedState' are incompatible.
    Type 'DeepPartial | undefined' is not assignable to type 'StoreEnhancer | undefined'.
      Type 'DeepPartial' is not assignable to type 'StoreEnhancer'.
        Type 'DeepPartial' provides no match for the signature '(next: StoreEnhancerStoreCreator<{}, {}>): StoreEnhancerStoreCreator'`

How can I resolve this?

Install on react native redux project failed

Hi, found this library, tried to incorporate it, didn't work for me (but I appreciate your taking the time to publish it!). I received the error below when trying to run project with this installed.

Setup: react-native project with redux, redux-saga.

I'm fairly new to this, so it's entirely possible there's some compatibility/extension/something related to ES2015 that I'm assumed to have installed but don't.

TransformError: /node_modules/redux-localstorage-simple/node_modules/immutable/dist/immutable.js: Couldn't find preset "es2015" relative to directory "/node_modules/redux-localstorage-simple"

RCTFatal
-[RCTBatchedBridge stopLoadingWithError:]
__25-[RCTBatchedBridge start]_block_invoke_2
_dispatch_call_block_and_release
_dispatch_client_callout
_dispatch_main_queue_callback_4CF
CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
__CFRunLoopRun
CFRunLoopRunSpecific
GSEventRunModal
UIApplicationMain
main
start

Dependency object-merge is missing

Build was failing for me in a separate app because it couldn't resolve object-merge, which was used in this package and isn't included in its package.json
The fix was to run npm install object-merge
I've got a branch but don't have permissions to push my branch and create a PR

Config question

Hi all,
How to config this with redux-thunk and redux devtools together?
Thank you

Could clear also have a debounce?

Not very important as the clear action can be debounced easily by the user, but for the sake of completion, could clear also have a debounce option?
If save is debounced, then clearing at a certain point in state history is overriden with the debounced save.

Need an example of Clear()

Whenever a user hit logout I need to clear the localStorage but I am not getting how to use clear function of it.

Any help is appreciated.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.