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

Allow for any type of root level state #113

Merged
merged 4 commits into from
Aug 28, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# What about redux-persist?
This is a fork of redux-persist published as `redux-persist-2` until the PR https://github.com/rt2zz/redux-persist/pull/113 gets merged.

# Redux Persist
Persist and rehydrate a redux store.

Expand Down
47 changes: 27 additions & 20 deletions src/autoRehydrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { REHYDRATE } from './constants'
import isStatePlainEnough from './utils/isStatePlainEnough'

export default function autoRehydrate (config = {}) {
config.stateReconciler = config.stateReconciler || defaultStateReconciler

return (next) => (reducer, initialState, enhancer) => {
return next(createRehydrationReducer(reducer), initialState, enhancer)
}
Expand All @@ -19,26 +21,8 @@ export default function autoRehydrate (config = {}) {

let inboundState = action.payload
let reducedState = reducer(state, action)
let newState = {...reducedState}

Object.keys(inboundState).forEach((key) => {
// if initialState does not have key, skip auto rehydration
if (!state.hasOwnProperty(key)) return

// if reducer modifies substate, skip auto rehydration
if (state[key] !== reducedState[key]) {
if (config.log) console.log('redux-persist/autoRehydrate: sub state for key `%s` modified, skipping autoRehydrate.', key)
newState[key] = reducedState[key]
return
}

// otherwise take the inboundState
if (isStatePlainEnough(inboundState[key]) && isStatePlainEnough(state[key])) newState[key] = {...state[key], ...inboundState[key]} // shallow merge
else newState[key] = inboundState[key] // hard set

if (config.log) console.log('redux-persist/autoRehydrate: key `%s`, rehydrated to ', key, newState[key])
})
return newState

return config.stateReconciler(state, inboundState, reducedState, config.log)
}
}
}
Expand All @@ -53,3 +37,26 @@ function logPreRehydrate (preRehydrateActions) {
`, preRehydrateActions.length)
}
}

function defaultStateReconciler (state, inboundState, reducedState, logger) {
let newState = {...reducedState}

Object.keys(inboundState).forEach((key) => {
// if initialState does not have key, skip auto rehydration
if (!state.hasOwnProperty(key)) return

// if reducer modifies substate, skip auto rehydration
if (state[key] !== reducedState[key]) {
if (logger) console.log('redux-persist/autoRehydrate: sub state for key `%s` modified, skipping autoRehydrate.', key)
newState[key] = reducedState[key]
return
}

// otherwise take the inboundState
if (isStatePlainEnough(inboundState[key]) && isStatePlainEnough(state[key])) newState[key] = {...state[key], ...inboundState[key]} // shallow merge
else newState[key] = inboundState[key] // hard set

if (logger) console.log('redux-persist/autoRehydrate: key `%s`, rehydrated to ', key, newState[key])
})
return newState
}
32 changes: 23 additions & 9 deletions src/createPersistor.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import * as constants from './constants'
import createAsyncLocalStorage from './defaults/asyncLocalStorage'
import isStatePlainEnough from './utils/isStatePlainEnough'
import stringify from 'json-stringify-safe'
import { forEach } from 'lodash'

export default function createPersistor (store, config) {
// defaults
const lastStateInit = config.lastStateInit || {}
const stateIterator = config.stateIterator || defaultStateIterator
const stateGetter = config.stateGetter || defaultStateGetter
const stateSetter = config.stateSetter || defaultStateSetter
const serialize = config.serialize || defaultSerialize
const deserialize = config.deserialize || defaultDeserialize
const blacklist = config.blacklist || []
Expand All @@ -18,7 +21,7 @@ export default function createPersistor (store, config) {
if (storage.keys && !storage.getAllKeys) storage = {...storage, getAllKeys: storage.keys}

// initialize stateful values
let lastState = {}
let lastState = lastStateInit
let paused = false
let purgeMode = false
let storesToProcess = []
Expand All @@ -28,13 +31,10 @@ export default function createPersistor (store, config) {
if (paused) return

let state = store.getState()
if (process.env.NODE_ENV !== 'production') {
if (!isStatePlainEnough(state)) console.warn('redux-persist: State is not plain enough to persist. Can only persist plain objects.')
}

forEach(state, (subState, key) => {
stateIterator(state, (subState, key) => {
if (!passWhitelistBlacklist(key)) return
if (lastState[key] === state[key]) return
if (stateGetter(lastState, key) === stateGetter(state, key)) return
if (storesToProcess.indexOf(key) !== -1) return
storesToProcess.push(key)
})
Expand All @@ -50,7 +50,7 @@ export default function createPersistor (store, config) {

let key = storesToProcess[0]
let storageKey = createStorageKey(key)
let endState = transforms.reduce((subState, transformer) => transformer.in(subState, key), store.getState()[storesToProcess[0]])
let endState = transforms.reduce((subState, transformer) => transformer.in(subState, key), stateGetter(store.getState(), key))
if (typeof endState !== 'undefined') storage.setItem(storageKey, serialize(endState), warnIfSetError(key))
storesToProcess.shift()
}, debounce)
Expand All @@ -71,9 +71,10 @@ export default function createPersistor (store, config) {
forEach(incoming, (subState, key) => {
try {
let data = deserialize(subState)
state[key] = transforms.reduceRight((interState, transformer) => {
let value = transforms.reduceRight((interState, transformer) => {
return transformer.out(interState, key)
}, data)
state = stateSetter(state, key, value)
} catch (err) {
if (process.env.NODE_ENV !== 'production') console.warn(`Error rehydrating data for key "${key}"`, subState, err)
}
Expand Down Expand Up @@ -152,3 +153,16 @@ function rehydrateAction (data) {
payload: data
}
}

function defaultStateIterator (collection, callback) {
return forEach(collection, callback)
}

function defaultStateGetter (state, key) {
return state[key]
}

function defaultStateSetter (state, key, value) {
state[key] = value
return state
}