-
Notifications
You must be signed in to change notification settings - Fork 864
/
Copy pathcreatePersistoid.ts
143 lines (125 loc) · 4.06 KB
/
createPersistoid.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* eslint-disable @typescript-eslint/no-explicit-any */
import { KEY_PREFIX } from './constants'
import type { Persistoid, PersistConfig } from './types'
import { KeyAccessState } from './types'
export default function createPersistoid(config: PersistConfig<any>): Persistoid {
// defaults
const blacklist: string[] | null = config.blacklist || null
const whitelist: string[] | null = config.whitelist || null
const transforms = config.transforms || []
const throttle = config.throttle || 0
const storageKey = `${
config.keyPrefix !== undefined ? config.keyPrefix : KEY_PREFIX
}${config.key}`
const storage = config.storage
let serialize: (x: any) => any
if (config.serialize === false) {
serialize = (x: any) => x
} else if (typeof config.serialize === 'function') {
serialize = config.serialize
} else {
serialize = defaultSerialize
}
const writeFailHandler = config.writeFailHandler || null
// initialize stateful values
let lastState: KeyAccessState = {}
const stagedState: KeyAccessState = {}
const keysToProcess: string[] = []
let timeIterator: any = null
let writePromise: Promise<any> | null = null
const update = (state: KeyAccessState) => {
// add any changed keys to the queue
Object.keys(state).forEach(key => {
if (!passWhitelistBlacklist(key)) return // is keyspace ignored? noop
if (lastState[key] === state[key]) return // value unchanged? noop
if (keysToProcess.indexOf(key) !== -1) return // is key already queued? noop
keysToProcess.push(key) // add key to queue
})
//if any key is missing in the new state which was present in the lastState,
//add it for processing too
Object.keys(lastState).forEach(key => {
if (
state[key] === undefined &&
passWhitelistBlacklist(key) &&
keysToProcess.indexOf(key) === -1 &&
lastState[key] !== undefined
) {
keysToProcess.push(key)
}
})
// start the time iterator if not running (read: throttle)
if (timeIterator === null) {
timeIterator = setInterval(processNextKey, throttle)
}
lastState = state
}
function processNextKey() {
if (keysToProcess.length === 0) {
if (timeIterator) clearInterval(timeIterator)
timeIterator = null
return
}
const key: any = keysToProcess.shift()
if (key === undefined) {
return
}
const endState = transforms.reduce((subState, transformer) => {
return transformer.in(subState, key, lastState)
}, lastState[key])
if (endState !== undefined) {
try {
stagedState[key] = serialize(endState)
} catch (err) {
console.error(
'redux-persist/createPersistoid: error serializing state',
err
)
}
} else {
//if the endState is undefined, no need to persist the existing serialized content
delete stagedState[key]
}
if (keysToProcess.length === 0) {
writeStagedState()
}
}
function writeStagedState() {
// cleanup any removed keys just before write.
Object.keys(stagedState).forEach(key => {
if (lastState[key] === undefined) {
delete stagedState[key]
}
})
writePromise = storage
.setItem(storageKey, serialize(stagedState))
.catch(onWriteFail)
}
function passWhitelistBlacklist(key: string) {
if (whitelist && whitelist.indexOf(key) === -1 && key !== '_persist')
return false
if (blacklist && blacklist.indexOf(key) !== -1) return false
return true
}
function onWriteFail(err: any) {
// @TODO add fail handlers (typically storage full)
if (writeFailHandler) writeFailHandler(err)
if (err && process.env.NODE_ENV !== 'production') {
console.error('Error storing data', err)
}
}
const flush = () => {
while (keysToProcess.length !== 0) {
processNextKey()
}
return writePromise || Promise.resolve()
}
// return `persistoid`
return {
update,
flush,
}
}
// @NOTE in the future this may be exposed via config
function defaultSerialize(data: any) {
return JSON.stringify(data)
}