Skip to content

Commit 196ed39

Browse files
feat: resync state
1 parent d7efde9 commit 196ed39

File tree

8 files changed

+115
-1
lines changed

8 files changed

+115
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ const App = () => {
100100
- pauses persistence
101101
- `.persist()`
102102
- resumes persistence
103+
- `.resync()`
104+
- overrides redux state with the value in storage
103105

104106
## State Reconciler
105107
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:

src/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
export const KEY_PREFIX = 'persist:'
44
export const FLUSH = 'persist/FLUSH'
55
export const REHYDRATE = 'persist/REHYDRATE'
6+
export const RESYNC = 'persist/RESYNC'
67
export const PAUSE = 'persist/PAUSE'
78
export const PERSIST = 'persist/PERSIST'
89
export const PURGE = 'persist/PURGE'

src/persistReducer.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
PURGE,
77
REHYDRATE,
88
DEFAULT_VERSION,
9+
RESYNC,
910
} from './constants'
1011

1112
import type {
@@ -155,6 +156,19 @@ export default function persistReducer<State: Object, Action: Object>(
155156
...baseReducer(restState, action),
156157
_persist,
157158
}
159+
} else if (action.type === RESYNC) {
160+
getStoredState(config)
161+
.then(
162+
restoredState =>
163+
action.rehydrate(config.key, restoredState, undefined),
164+
err => action.rehydrate(config.key, undefined, err)
165+
)
166+
.then(() => action.result())
167+
168+
return {
169+
...baseReducer(restState, action),
170+
_persist: { version, rehydrated: false },
171+
}
158172
} else if (action.type === FLUSH) {
159173
action.result(_persistoid && _persistoid.flush())
160174
return {

src/persistStore.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
} from './types'
1212

1313
import { createStore } from 'redux'
14-
import { FLUSH, PAUSE, PERSIST, PURGE, REGISTER, REHYDRATE } from './constants'
14+
import { FLUSH, PAUSE, PERSIST, RESYNC, PURGE, REGISTER, REHYDRATE } from './constants'
1515

1616
type PendingRehydrate = [Object, RehydrateErrorType, PersistConfig]
1717
type Persist = <R>(PersistConfig, MigrationManifest) => R => R
@@ -120,6 +120,15 @@ export default function persistStore(
120120
persist: () => {
121121
store.dispatch({ type: PERSIST, register, rehydrate })
122122
},
123+
resync: () => {
124+
return new Promise(resolve => {
125+
store.dispatch({
126+
type: RESYNC,
127+
rehydrate,
128+
result: () => resolve(),
129+
})
130+
})
131+
},
123132
}
124133

125134
if (!(options && options.manualPersist)){

src/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ type PersistorSubscribeCallback = () => any
8181
export type Persistor = {
8282
pause: () => void,
8383
persist: () => void,
84+
resync: () => Promise<void>,
8485
purge: () => Promise<any>,
8586
flush: () => Promise<any>,
8687
+dispatch: PersistorAction => PersistorAction,

tests/resync.spec.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// @flow
2+
3+
import test from 'ava'
4+
import sinon from 'sinon'
5+
6+
import _ from 'lodash'
7+
import configureStore from 'redux-mock-store'
8+
import { createStore } from 'redux'
9+
10+
import getStoredState from '../src/getStoredState'
11+
import persistReducer from '../src/persistReducer'
12+
import persistStore from '../src/persistStore'
13+
import { createMemoryStorage } from 'storage-memory'
14+
import { PERSIST, REHYDRATE, FLUSH } from '../src/constants'
15+
import sleep from './utils/sleep'
16+
17+
const initialState = { a: 0 }
18+
const persistObj = {
19+
version: 1,
20+
rehydrated: true
21+
};
22+
23+
let reducer = (state = initialState, { type }) => {
24+
console.log('action', type)
25+
if (type === 'INCREMENT') {
26+
return _.mapValues(state, v => v + 1)
27+
}
28+
return state
29+
}
30+
31+
const memoryStorage = createMemoryStorage()
32+
33+
const config = {
34+
key: 'resync-reducer-test',
35+
version: 1,
36+
storage: memoryStorage,
37+
debug: true,
38+
throttle: 1000,
39+
}
40+
41+
test('state is resync from storage', t => {
42+
return new Promise((resolve, reject) => {
43+
let rootReducer = persistReducer(config, reducer)
44+
const store = createStore(rootReducer)
45+
46+
const persistor = persistStore(store, {}, async () => {
47+
48+
// 1) Make sure redux-persist and storage are in the same state
49+
50+
await persistor.flush();
51+
let storagePreModify = await getStoredState(config)
52+
53+
const oldStorageState = {
54+
...initialState,
55+
_persist: persistObj,
56+
};
57+
t.deepEqual(
58+
storagePreModify,
59+
oldStorageState
60+
)
61+
62+
// 2) Change the storage directly (so redux-persist won't notice it changed)
63+
64+
const newStorageValue = {
65+
a: 1, // override the value of a
66+
_persist: JSON.stringify(persistObj),
67+
}
68+
await memoryStorage.setItem(`persist:${config.key}`, JSON.stringify(newStorageValue));
69+
let storagePostModify = await getStoredState(config)
70+
71+
// 3) Call resync and make sure redux-persist state was overriden by storage content
72+
73+
await persistor.resync();
74+
t.deepEqual(
75+
storagePostModify,
76+
{
77+
a: 1,
78+
_persist: persistObj,
79+
}
80+
)
81+
82+
resolve()
83+
})
84+
})
85+
})

types/constants.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ declare module "redux-persist/es/constants" {
22
const KEY_PREFIX: 'persist:';
33
const FLUSH: 'persist/FLUSH';
44
const REHYDRATE: 'persist/REHYDRATE';
5+
const RESYNC: 'persist/RESYNC';
56
const PAUSE: 'persist/PAUSE';
67
const PERSIST: 'persist/PERSIST';
78
const PURGE: 'persist/PURGE';

types/types.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ declare module "redux-persist/es/types" {
137137
interface Persistor {
138138
pause(): void;
139139
persist(): void;
140+
resync(): Promise<void>;
140141
purge(): Promise<any>;
141142
flush(): Promise<any>;
142143
dispatch(action: PersistorAction): PersistorAction;

0 commit comments

Comments
 (0)