Перейти к содержимому

Redux persist что это

  • автор:

3. React, TypeScript, Redux Toolkit: configureStore, createSlice, middleWare Redux Thunk

Redux-persist предоставляет различные хранилища для сохранения данных, такие как LocalStorage , SessionStorage .

Redux Persist берет ваш объект состояния Redux ( initialState из reducer ) и сохраняет его в постоянное хранилище ( LocalStorage или SessionStorage ). Затем при запуске приложения он извлекает это постоянное состояние и сохраняет его обратно в redux.

Дополнительно

0. Базовая настройка и оптимизация

1. Установка и настройка фундамента проекта

Разъясните пож. как работают методы Redux Persist

Знаете кого-то, кто может ответить? Поделитесь ссылкой на этот вопрос по почте, через Твиттер или Facebook.

    Важное на Мете
Похожие

Подписаться на ленту

Лента вопроса

Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.

Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.10.27.43697

Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.

Saved searches

Use saved searches to filter your results more quickly

Cancel Create saved search

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

persist and rehydrate a redux store

License

rt2zz/redux-persist

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Switch branches/tags
Branches Tags
Could not load branches
Nothing to show
Could not load tags
Nothing to show

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Cancel Create

  • Local
  • Codespaces

HTTPS GitHub CLI
Use Git or checkout with SVN using the web URL.
Work fast with our official CLI. Learn more about the CLI.

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

d8b01a0 Nov 23, 2021

That was entirely my bad. Thanks for catching it. *facepalm* �� Remove duplicate section of readme

Git stats

Files

Failed to load latest commit information.

Latest commit message
Commit time
August 7, 2021 22:14
January 25, 2019 17:04
August 9, 2021 14:14
August 7, 2021 19:44
August 9, 2021 14:14
August 8, 2021 14:03
October 17, 2021 11:20
August 8, 2021 16:36
August 7, 2021 23:43
August 9, 2021 11:23
August 7, 2021 19:44
October 17, 2021 11:16
September 1, 2019 18:39
October 31, 2017 08:24
October 24, 2021 12:11
October 17, 2021 11:16
October 17, 2021 11:16
August 9, 2021 11:23
August 9, 2021 14:52

README.md

Redux Persist

Persist and rehydrate a redux store.

October 15th, 2021 — Move to TypeScript (Thanks @smellman)

As part of the work to upgrade the infrastructure used to build redux-persist, we’re moving from Flow to TypeScript.

  • Move from Flow to TypeScript
  • Move from TravisCI to GitHub Actions (.github/workflows/ci.yml)
  • Version updates for some dependencies

September 22nd, 2021 — Under New Management

Redux Persist is a staple project for Redux developers, both on mobile and on the web. If you’re here, it’s likely because you need it now or have used it before and need to debug something, and like me have possibly struggled with making it work (especially with newer versions of things) and making it work with your code because the examples you’ll find around the internet are inconsistent.

I (@ckalika) spoke with @rt2zz about taking over maintenance of the project, and we agreed to give it a shot and see how we go. My priorities are as follows:

  1. Go through and triage the existing issues
    • Separate them into bugs, feature requests, basic questions/requests for code samples, and issues that are either not project-specific or don’t fall within the remit of the project (specific definitions and criteria will be posted in the future)
    • Determine the severity/urgency of each bug or feature request
    • Guestimate the size of them
    • Determine which are actionable immediately or in the short term
    • Establish some semblance of test criteria for each
  2. Upgrade dependencies (where possible) so that we’ve got something building with modern versions
    • Note: Right now, it’s about modernising the project infrastructure and build process without making breaking API changes
  3. Go through the existing pull requests
    • Merge the ones that deal with documentation, code samples, etc.
    • Review and merge the ones that deal with open issues
    • Review and merge the ones that will require breaking changes and consult authors about redux-persist@v7 (feature set and requirements to be defined)
  4. Update the documentation
    • Split it out for both web and mobile
    • Providing code samples and test coverage for how to use the library
    • Provide or link to working examples that integrate with additional libraries (e.g. RTK Query).
  5. Improve testing and automation
    • Move to GitHub Actions
    • Move from Ava to Jest

There’s a lot to do here, so I’ll ask your patience and understanding as I work through it. If you have ideas for how to improve the library, the documentation, or the community, I’d love to hear them, and if you’re submitting pull requests (or have submitted some previously), please reach out and help me understand what you’re aiming to do with it.

I’ll try to get some discussions up to pull together ideas, so we can properly work out what the next version is likely to look like.

v6 upgrade

Web: no breaking changes React Native: Users must now explicitly pass their storage engine in. e.g.

import AsyncStorage from '@react-native-async-storage/async-storage'; const persistConfig =  //. storage: AsyncStorage >

Quickstart

npm install redux-persist

  1. Basic Usage
  2. Nested Persists
  3. Hot Module Replacement
  4. Code Splitting [coming soon]
Basic Usage

Basic usage involves adding persistReducer and persistStore to your setup. IMPORTANT Every app needs to decide how many levels of state they want to «merge». The default is 1 level. Please read through the state reconciler docs for more information.

// configureStore.js import  createStore > from 'redux' import  persistStore, persistReducer > from 'redux-persist' import storage from 'redux-persist/lib/storage' // defaults to localStorage for web import rootReducer from './reducers' const persistConfig =  key: 'root', storage, > const persistedReducer = persistReducer(persistConfig, rootReducer) export default () =>  let store = createStore(persistedReducer) let persistor = persistStore(store) return  store, persistor > >

If you are using react, wrap your root component with PersistGate. This delays the rendering of your app’s UI until your persisted state has been retrieved and saved to redux. NOTE the PersistGate loading prop can be null, or any react instance, e.g. loading=>

import  PersistGate > from 'redux-persist/integration/react' // . normal setup, create store and persistor, import components etc. const App = () =>  return ( Provider store=store>> PersistGate loading=null> persistor=persistor>> RootComponent /> /PersistGate> /Provider> ); >;

API

persistReducer(config, reducer)
  • arguments
    • configobject
      • required config: key, storage
      • notable other config: whitelist, blacklist, version, stateReconciler, debug
      • any reducer will work, typically this would be the top level reducer returned by combineReducers
      persistStore(store, [config, callback])
      • arguments
        • storeredux store The store to be persisted.
        • configobject (typically null)
          • If you want to avoid that the persistence starts immediately after calling persistStore , set the option manualPersist. Example: < manualPersist: true >Persistence can then be started at any point with persistor.persist() . You usually want to do this if your storage is not ready when the persistStore call is made.
          persistor object
          • the persistor object is returned by persistStore with the following methods:
            • .purge()
              • purges state from disk and returns a promise
              • immediately writes all pending state to disk and returns a promise
              • pauses persistence
              • resumes persistence

              State Reconciler

              State reconcilers define how incoming state is merged in with initial state. It is critical to choose the right state reconciler for your state. There are three options that ship out of the box, let’s look at how each operates:

              1. hardSet ( import hardSet from ‘redux-persist/lib/stateReconciler/hardSet’ ) This will hard set incoming state. This can be desirable in some cases where persistReducer is nested deeper in your reducer tree, or if you do not rely on initialState in your reducer.
                • incoming state:
                • initial state:
                • reconciled state: < foo: incomingFoo >// note bar has been dropped
              2. autoMergeLevel1 (default) This will auto merge one level deep. Auto merge means if the some piece of substate was modified by your reducer during the REHYDRATE action, it will skip this piece of state. Level 1 means it will shallow merge 1 level deep.
                • incoming state:
                • initial state:
                • reconciled state: < foo: incomingFoo, bar: initialBar >// note incomingFoo overwrites initialFoo
              3. autoMergeLevel2 ( import autoMergeLevel2 from ‘redux-persist/lib/stateReconciler/autoMergeLevel2’ ) This acts just like autoMergeLevel1, except it shallow merges two levels
                • incoming state:
                • initial state:
                • reconciled state: < foo: mergedFoo, bar: initialBar >// note: initialFoo and incomingFoo are shallow merged
              Example
              import hardSet from 'redux-persist/lib/stateReconciler/hardSet' const persistConfig =  key: 'root', storage, stateReconciler: hardSet, >

              React Integration

              Redux persist ships with react integration as a convenience. The PersistGate component is the recommended way to delay rendering until persistence is complete. It works in one of two modes:

              1. loading prop: The provided loading value will be rendered until persistence is complete at which point children will be rendered.
              2. function children: The function will be invoked with a single bootstrapped argument. When bootstrapped is true, persistence is complete and it is safe to render the full app. This can be useful for adding transition animations.

              Blacklist & Whitelist

              // BLACKLIST const persistConfig =  key: 'root', storage: storage, blacklist: ['navigation'] // navigation will not be persisted >; // WHITELIST const persistConfig =  key: 'root', storage: storage, whitelist: ['navigation'] // only navigation will be persisted >;

              Nested Persists

              Nested persist can be useful for including different storage adapters, code splitting, or deep filtering. For example while blacklist and whitelist only work one level deep, but we can use a nested persist to blacklist a deeper value:

              import  combineReducers > from 'redux' import  persistReducer > from 'redux-persist' import storage from 'redux-persist/lib/storage' import  authReducer, otherReducer > from './reducers' const rootPersistConfig =  key: 'root', storage: storage, blacklist: ['auth'] > const authPersistConfig =  key: 'auth', storage: storage, blacklist: ['somethingTemporary'] > const rootReducer = combineReducers( auth: persistReducer(authPersistConfig, authReducer), other: otherReducer, >) export default persistReducer(rootPersistConfig, rootReducer)

              Migrations

              persistReducer has a general purpose «migrate» config which will be called after getting stored state but before actually reconciling with the reducer. It can be any function which takes state as an argument and returns a promise to return a new state object.

              Redux Persist ships with createMigrate , which helps create a synchronous migration for moving from any version of stored state to the current state version. [Additional information]

              Transforms

              Transforms allow you to customize the state object that gets persisted and rehydrated.

              There are several libraries that tackle some common implementations for transforms.

              • immutable — support immutable reducers
              • seamless-immutable — support seamless-immutable reducers
              • compress — compress your serialized state with lz-string
              • encrypt — encrypt your serialized state with AES
              • filter — store or load a subset of your state
              • filter-immutable — store or load a subset of your state with support for immutablejs
              • expire — expire a specific subset of your state based on a property
              • expire-reducer — more flexible alternative to expire transformer above with more options

              When the state object gets persisted, it first gets serialized with JSON.stringify() . If parts of your state object are not mappable to JSON objects, the serialization process may transform these parts of your state in unexpected ways. For example, the javascript Set type does not exist in JSON. When you try to serialize a Set via JSON.stringify() , it gets converted to an empty object. Probably not what you want.

              Below is a Transform that successfully persists a Set property, which simply converts it to an array and back. In this way, the Set gets converted to an Array, which is a recognized data structure in JSON. When pulled out of the persisted store, the array gets converted back to a Set before being saved to the redux store.

              import  createTransform > from 'redux-persist'; const SetTransform = createTransform( // transform state on its way to being serialized and persisted. (inboundState, key) =>  // convert mySet to an Array. return  . inboundState, mySet: [. inboundState.mySet] >; >, // transform state being rehydrated (outboundState, key) =>  // convert mySet back to a Set. return  . outboundState, mySet: new Set(outboundState.mySet) >; >, // define which reducers this transform gets called for.  whitelist: ['someReducer'] > ); export default SetTransform;

              The createTransform function takes three parameters.

              1. An «inbound» function that gets called right before state is persisted (optional).
              2. An «outbound» function that gets called right before state is rehydrated (optional).
              3. A config object that determines which keys in your state will be transformed (by default no keys are transformed).

              In order to take effect transforms need to be added to a PersistReducer ’s config object.

              import storage from 'redux-persist/lib/storage'; import < SetTransform >from './transforms'; const persistConfig = < key: 'root', storage: storage, transforms: [SetTransform] >; 

              Storage Engines

              • localStorage import storage from ‘redux-persist/lib/storage’
              • sessionStorage import storageSession from ‘redux-persist/lib/storage/session’
              • electron storage Electron support via electron store
              • redux-persist-cookie-storage Cookie storage engine, works in browser and Node.js, for universal / isomorphic apps
              • redux-persist-expo-filesystem react-native, similar to redux-persist-filesystem-storage but does not require linking or ejecting CRNA/Expo app. Only available if using Expo SDK (Expo, create-react-native-app, standalone).
              • redux-persist-expo-securestore react-native, for sensitive information using Expo’s SecureStore. Only available if using Expo SDK (Expo, create-react-native-app, standalone).
              • redux-persist-fs-storage react-native-fs engine
              • redux-persist-filesystem-storage react-native, to mitigate storage size limitations in android (#199, #284) redux-persist-indexeddb-storage recommended for web via localForage
              • redux-persist-node-storage for use in nodejs environments.
              • redux-persist-pouchdb Storage engine for PouchDB.
              • redux-persist-sensitive-storage react-native, for sensitive information (uses react-native-sensitive-info).
              • redux-persist-weapp-storage Storage engine for wechat mini program, also compatible with wepy
              • redux-persist-webextension-storage Storage engine for browser (Chrome, Firefox) web extension storage
              • @bankify/redux-persist-realm Storage engine for Realm database, you will need to install Realm first
              • custom any conforming storage api implementing the following methods: setItem getItem removeItem . (NB: These methods must support promises)

              Community & Contributing

              I will be updating this section shortly. If you have a pull request that you’ve got outstanding, please reach out and I will try to review it and get it integrated. As we’ve shifted to TypeScript, that may necessitate some changes, but I’m happy to help in that regard, wherever I can.

              About

              persist and rehydrate a redux store

              Inscrivez-vous à notre newsletter

              Le BAM Innovation Lab, pour rester à la pointe des dernières innovations tech et produit !

              Votre inscription a bien été prise en compte !

              Vous avez un projet mobile ?

              Parlons-en ensemble

              Consultez notre offre

              Lorem Ipsum
              Exemple Offre

              Intro

              I never really understood one of the four Redux essential best practices, nor did I actually really try to. Until I had to learn it the hard way, when I was facing a weird bug.

              I had this JavaScript error after calling the getTime method of myDate, a persisted Date object:

              Capture d'e?cran 2021-03-20 a? 10.28.46

              As I did not understand where it came from, I did what most serious developers in this situation do: I ran my code again without making any change, hoping for it to work. It did not.

              Actually, redux-persist documentation explains that they are not able to persist non-serializable values, like my Date object apparently is.

              But that’s fine because they should never have been in our stores anyway, persistence or not: it is a redux best practice: «Do Not Put Non-Serializable Values in State or Actions». Just like «Promises, Symbols, Maps/Sets, functions, or class instances», Date objects should not be saved in our stores. This is one of the four essential best practices.

              Some reasons for this best practice were very well explained. But for some of them, I had to dig a little bit further. So I thought I’d share what I’ve learnt along the road.

              First, let’s make a quick recap.

              Let’s align on what serialization means

              Serialization is the process of formatting a data to fit another data structure, to be able to store it and retrieve it later.

              In our case, we are talking about JSON-serialization.

              To serialize an object to JSON, you can use JSON.stringify:

              ++pre>++code>const myStringifiedObject = JSON.stringify(< it: 'works' >); // ++/code>++/pre>

              And to turn your object back to JS, you can use JSON.parse:

              But those methods don’t support every type of data. For example, Sets don’t have their own representation in JSON, and will be turned into an empty objects:

              ++pre>++code>const myStringifiedSet = JSON.stringify(new Set([1, 2, 3])); // <>++/code>++/pre>

              Which means that when you retrieve it, you get an empty object:

              You lost your information!

              Those types of data that don’t have any representation in JSON are called non-serializable. If you serialize and then unserialize those types of entities, you won’t end up with the correct data structure, and may loose some informations.

              Now that we are clear about what serialization means, let’s get to the heart of the matter.

              There are three reasons why you should not put non-serializable values in your state nor in your actions.

              Reason 1: To make sure the UI gets updated as expected

              In the definition of the best practice, it is written that not using non-serializable values «ensures that the UI will update as expected». Let’s take a look at what it means in short, and we will break things down afterward:

              A non-serializable value is a complex object, like a class instance or a function. It is not an array, a plain serializable object, nor a primitive (like strings, numbers, booleans, null, etc.). Otherwise, it would be included in the list of the items that JSON supports.

              As it is a complex object, you have more chances to mutate it. (I’ll explain why in a sec).

              If you mutate an object, redux might not be able to trigger a re-render on your React component, and you might therefore have old, incoherent data in your UI.

              Now that we have the big picture, let’s dig a little bit further: first, why would we especially end up mutating non-serializable data?

              You would mutate your non-serializable data

              Let’s say you have a Set of numbers in your store. You want to be able to add a new number to the Set. How do you handle this in your reducer?

              Well, you certainly cannot use the Set’s add method, as it would mutate your Set. Nor do you have access to any of the very accommodating JavaScript array functions, map, filter, etc. As they all return a new array with no mutation, they are perfect to be used in a reducer, but unfortunately, there is no equivalent on Sets.

              What you could do though, is to turn your set into an array, use your favorite JavaScript array function to add your number, and turn this one into a Set. And there you go, you’ve got your new Set!

              ++pre>++code data-line-start=»45″ data-line-end=»47″>const newSet = new Set([. mySet, newNumber])
              ++/code>++/pre>

              That is the opposite of efficiency. And it’s exhausting. This is why one of your development team member would be more at risk of mutating your Set by simply doing:

              ++pre>++code data-line-start=»45″ data-line-end=»47″>const newSet = mySet.add(newNumber)++/code>++/pre>

              It is even more true for some other non-serializable items the doc mentions. Think of the easiest way to change a class instance without mutating it.

              Feel free to check out other data structures in the list, you will come to the conclusion that you have more chances to mutate non-serializable data by accident, because you don’t really have any other easy workaround. And the first Redux best practice is very clear: «Do Not Mutate State».

              Mutation is evil for redux

              And one (and not the least) of the consequences of mutation is inconsistent UI: a mutation over a non primitive value will not trigger a re-render, you will still see in your component the previous value, before it was mutated. It would result in inconsistent UI: the UI doesn’t reflect the data in your store.

              Is this reason legitimate?

              Mutation on a non-serializable value is not any different from mutation on an array. So one may wonder, is this UI update reason good enough to avoid non-serializable values?

              Well, you can mutate an array, but arrays, and even objects, have plenty of methods here to help you create values easily without mutations, that non-serializable data don’t have. The difference lays here, you are way more likely to mutate a class or a Set, because you can’t really do otherwise, that’s what you do with those objects.

              But what if those are not valid arguments for you? If you do not need to mutate it, nor to painfully change it as we tried with our set? Well, maybe you don’t need to store this value in your store at all.

              Reason 2: You don’t (always) need to save this type of data in a store

              To know if I really need non-serializable data in my store, I first have to be able to identify them.

              What are the non-serializable data

              Well pretty much nothing is serializable, except plain JS Objects, Arrays and Primitives. The best practice definition also gives us a list of examples: Promises, Symbols, Maps/Sets, functions, and class instances. Some of you might have noticed that the Date object I struggled with was actually in that list. Look closely.

              Indeed, to initialize a Date object, I have to use the «new» keyword. That makes it a class instance. You have to be aware that every time you create a value by starting with the «new» keyword, it is actually an instance of some kind of class. So when they say «Promises and Symbols, Maps/Sets and class instances», they actually say «class instances».

              So class instances are generally not serializable, and should not be found in our store. I say generally because there are exceptions: Objects and Arrays are serializable, and are perfectly safe to be put in our stores.

              Plus, class instances are a perfect example of data types that you don’t need in your store!

              class instances workaround

              Take my Date object for example. It has plenty of setter methods, like setDate. But they involve mutation, so I can not use them. The only really useful information is actually its timestamp. So I don’t need to keep a Date object in my store, I can just save timestamp: it gives me as much information as a Date object would. And I can convert my timestamp into a Date when I have to use Date’s methods.

              We can generalize this reasoning to all class instances.

              In a class, you have two kinds of elements: the interesting data (like the timestamp of my date), and a bunch of methods that will never change, they are just pure logic. Why would you save those in a store? Placing this unchanging logic in your store will be of no use. Instead, you can convert your methods into logic handled either directly by the reducer, or completely outside of your store, depending on what they do, and put the interesting data inside your store.

              Serializable workarounds

              If you are interested in further exploring what your favorite non-serializable data structure might look like if properly used in a store, this article gives you some workarounds to save only the interesting part in your store.

              Most of the time, you don’t need to save non-serializable data in your store, or at least there are some workarounds. That being said, there might also be some cases where you have no choice but to store non-serializable data.

              What if I really need my non-serializable data?

              Some non-serializable data cannot be avoided. It can be related to the use of a library, for example.
              You might need it, and be very conscious about not mutating it. Are there still some counter-arguments, then?
              As explained in the documentation, this best practice is just a guideline. As long as you know what you are doing with your data, and as long as you are not mutating it, you should be just fine! If you really need your non-serializable data, you can use a library that enables you to use non-serializable data types, like Maps and Sets, and provides functions that don’t involve mutation.

              Nevertheless, you have to be aware that some redux extensions features might not work.

              Reason 3: To be able to use redux extensions

              This is actually a very important reason for the best practice, but it is not the one that required me to dig the most, as it is already very well documented, so I won’t develop this part much.
              What you need to know, is that redux extensions, like Redux Persist or Redux DevTools, expect you to follow this essential best practice, as they themselves need serialized data to work properly.

              About redux persist

              To be able to retrieve your values when the app is killed, or the machine turned off, redux-persist will store them in a storage engine, like the local storage on the web. On those engines, every item has to be stored as strings. So redux-persist is going to JSON-format your data, to change it into a string.

              To do so, they call JSON.stringify when storing your value, and JSON.parse when retrieving it to transform it back to its original data structure.
              So what happened to my beloved Date object?

              Date object, like other non-serializable values, don’t have any representation in JSON format. When JSON.stringify was called on my Date object, it returned a string timestamp:

              ++pre>++code>const myStringifiedDate = JSON.stringify(new Date(‘2000-01-01’)); // «2000-01-01T00:00:00.000Z»++/code>++/pre>

              This string was then stored in the local storage. When I retrieved my value, JSON.parse just returned the ISOString, not a Date:

              So when tried to call getTime on my variable, it threw an error, as this method does not exist on strings.

              What the redux best practice encourages to do instead, is to store a string or number timestamp, and to convert it into a Date when needed.

              About redux devtools

              Another very important feature of redux, which depends on serialization, is time-travel debugging. And it is actually the reason why you should not have non-serializable actions, in addition to the state. The reason for that is very well explained here.

              Conclusion

              I hope the reasons for this best practice are more clear now. All of that being said, if you’re not interested in redux extensions, and as long as you are careful about mutation, you can have non-serializable values in your store. In the end, this is just a guideline.
              Just please consider using a timestamp next time you have to store a Date.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *