Skip to content

Commit

Permalink
Make library work with Immutable.js stores
Browse files Browse the repository at this point in the history
  • Loading branch information
okjulian committed Apr 13, 2017
1 parent 3bf42d3 commit 592a01f
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 16 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,10 +455,12 @@ Background sync is not yet supported. Coming soon.
#### Use an [Immutable](https://facebook.github.io/immutable-js/) store
Stores that implement the entire store as an Immutable.js structure are currently not supported. You can use Immutable in the rest of your store, but the root object and the `offline` state branch created by Redux Offline currently needs to be vanilla JavaScript objects.
[Contributions welcome](#contributing).
To use an immutable store, just override `config.immutable`:
```js
const config = {
immutable: true
}
```
## Contributing
Expand Down
12 changes: 10 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ var _redux = require('redux');

var _reduxPersist = require('redux-persist');

var _reduxPersistImmutable = require('redux-persist-immutable');

var _middleware = require('./middleware');

var _updater = require('./updater');
Expand All @@ -25,6 +27,7 @@ var babelPluginFlowReactPropTypes_proptype_Config = require('./types').babelPlug

// eslint-disable-next-line no-unused-vars
var persistor = void 0;
var autoRehydrate = _reduxPersist.autoRehydrate;

var createOfflineStore = exports.createOfflineStore = function createOfflineStore(reducer, preloadedState, enhancer) {
var userConfig = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
Expand All @@ -36,12 +39,17 @@ var createOfflineStore = exports.createOfflineStore = function createOfflineStor

// wraps userland reducer with a top-level
// reducer that handles offline state updating
var offlineReducer = (0, _updater.enhanceReducer)(reducer);
var offlineReducer = (0, _updater.enhanceReducer)(reducer, config);

var offlineMiddleware = (0, _redux.applyMiddleware)((0, _middleware.createOfflineMiddleware)(config));

if (config.immutable) {
autoRehydrate = _reduxPersistImmutable.autoRehydrate;
persistor = _reduxPersistImmutable.persistStore;
}

// create autoRehydrate enhancer if required
var offlineEnhancer = config.persist && config.rehydrate ? (0, _redux.compose)(offlineMiddleware, enhancer, (0, _reduxPersist.autoRehydrate)()) : (0, _redux.compose)(offlineMiddleware, enhancer);
var offlineEnhancer = config.persist && config.rehydrate ? (0, _redux.compose)(offlineMiddleware, enhancer, autoRehydrate()) : (0, _redux.compose)(offlineMiddleware, enhancer);

// create store
var store = (0, _redux.createStore)(offlineReducer, preloadedState, offlineEnhancer);
Expand Down
6 changes: 6 additions & 0 deletions lib/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ var createOfflineMiddleware = exports.createOfflineMiddleware = function createO

// find any actions to send, if any
var state = store.getState();
if (config.immutable) {
if (!state.toJS) {
throw new Error('Config.immutable is set to true but your root state is not immutable');
}
state = state.toJS();
}
var actions = take(state, config);

// if the are any actions in the queue that we are not
Expand Down
4 changes: 3 additions & 1 deletion lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ Object.defineProperty(module.exports, "babelPluginFlowReactPropTypes_proptype_Ap
retryCount: require("react").PropTypes.number.isRequired,
retryToken: require("react").PropTypes.number.isRequired,
retryScheduled: require("react").PropTypes.bool.isRequired
}).isRequired
}).isRequired,
toJS: require("react").PropTypes.func
})
});
Object.defineProperty(module.exports, "babelPluginFlowReactPropTypes_proptype_Config", {
Expand All @@ -160,6 +161,7 @@ Object.defineProperty(module.exports, "babelPluginFlowReactPropTypes_proptype_Co
effect: require("react").PropTypes.func.isRequired,
retry: require("react").PropTypes.func.isRequired,
discard: require("react").PropTypes.func.isRequired,
immutable: require("react").PropTypes.bool.isRequired,
persistOptions: require("react").PropTypes.shape({}).isRequired
})
});
12 changes: 10 additions & 2 deletions lib/updater.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var babelPluginFlowReactPropTypes_proptype_ResultAction = require('./types').babelPluginFlowReactPropTypes_proptype_ResultAction || require('react').PropTypes.any;
var babelPluginFlowReactPropTypes_proptype_Config = require('./types').babelPluginFlowReactPropTypes_proptype_Config || require('react').PropTypes.any;
/* global */

var babelPluginFlowReactPropTypes_proptype_ResultAction = require('./types').babelPluginFlowReactPropTypes_proptype_ResultAction || require('react').PropTypes.any;

var babelPluginFlowReactPropTypes_proptype_OfflineAction = require('./types').babelPluginFlowReactPropTypes_proptype_OfflineAction || require('react').PropTypes.any;

var babelPluginFlowReactPropTypes_proptype_OfflineState = require('./types').babelPluginFlowReactPropTypes_proptype_OfflineState || require('react').PropTypes.any;
Expand Down Expand Up @@ -85,14 +87,20 @@ var offlineUpdater = function offlineUpdater() {
return state;
};

var enhanceReducer = function enhanceReducer(reducer) {
var enhanceReducer = function enhanceReducer(reducer, config) {
return function (state, action) {
var offlineState = void 0;
var restState = void 0;
if (typeof state !== 'undefined') {
var offline = state.offline,
rest = _objectWithoutProperties(state, ['offline']);

if (config.immutable) {
offlineState = state.get('offline');
restState = state.delete('offline');

return reducer(restState, action).set('offline', offlineUpdater(offlineState, action));
}
offlineState = offline;
restState = rest;
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"redux-logger": "^2.8.2"
},
"dependencies": {
"redux-persist": "^4.5.0"
"redux-persist": "^4.5.0",
"redux-persist-immutable": "^4.2.0"
},
"peerDependencies": {
"redux": ">=3"
Expand Down
14 changes: 12 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
/*global $Shape*/
import type { Config } from './types';
import { applyMiddleware, createStore, compose } from 'redux';
import { autoRehydrate } from 'redux-persist';
import { autoRehydrate as reduxAutoRehydrate } from 'redux-persist';
import {
persistStore as immutablePersistStore,
autoRehydrate as immutableAutoRehydrateImmutable
} from 'redux-persist-immutable';
import { createOfflineMiddleware } from './middleware';
import { enhanceReducer } from './updater';
import { applyDefaults } from './config';
Expand All @@ -12,6 +16,7 @@ import { networkStatusChanged } from './actions';

// eslint-disable-next-line no-unused-vars
let persistor;
let autoRehydrate = reduxAutoRehydrate;

export const createOfflineStore = (
reducer: any,
Expand All @@ -26,10 +31,15 @@ export const createOfflineStore = (

// wraps userland reducer with a top-level
// reducer that handles offline state updating
const offlineReducer = enhanceReducer(reducer);
const offlineReducer = enhanceReducer(reducer, config);

const offlineMiddleware = applyMiddleware(createOfflineMiddleware(config));

if (config.immutable) {
autoRehydrate = immutableAutoRehydrateImmutable;
persistor = immutablePersistStore;
}

// create autoRehydrate enhancer if required
const offlineEnhancer = config.persist && config.rehydrate
? compose(offlineMiddleware, enhancer, autoRehydrate())
Expand Down
8 changes: 7 additions & 1 deletion src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ export const createOfflineMiddleware = (config: Config) => (store: any) => (next
const result = next(action);

// find any actions to send, if any
const state: AppState = store.getState();
let state: AppState = store.getState();
if (config.immutable) {
if (!state.toJS) {
throw new Error('Config.immutable is set to true but your root state is not immutable');
}
state = state.toJS();
}
const actions = take(state, config);

// if the are any actions in the queue that we are not
Expand Down
4 changes: 3 additions & 1 deletion src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ export type OfflineState = {
};

export type AppState = {
offline: OfflineState
offline: OfflineState,
toJS?: Function // Any better way to say that State can be Immutable?
};

type NetworkCallback = (result: boolean) => void;
Expand All @@ -55,5 +56,6 @@ export type Config = {
effect: (effect: any, action: OfflineAction) => Promise<*>,
retry: (action: OfflineAction, retries: number) => ?number,
discard: (error: any, action: OfflineAction, retries: number) => boolean,
immutable: boolean,
persistOptions: {}
};
11 changes: 9 additions & 2 deletions src/updater.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow
/* global */

import type { OfflineState, OfflineAction, ResultAction } from './types';
import type { OfflineState, OfflineAction, ResultAction, Config } from './types';
import {
OFFLINE_STATUS_CHANGED,
OFFLINE_SCHEDULE_RETRY,
Expand Down Expand Up @@ -79,11 +79,18 @@ const offlineUpdater = function offlineUpdater(
return state;
};

export const enhanceReducer = (reducer: any) => (state: any, action: any) => {
export const enhanceReducer = (reducer: any, config: Config) => (state: any, action: any) => {
let offlineState;
let restState;
if (typeof state !== 'undefined') {
const { offline, ...rest } = state;
if (config.immutable) {
offlineState = state.get('offline');
restState = state.delete('offline');

return reducer(restState, action)
.set('offline', offlineUpdater(offlineState, action));
}
offlineState = offline;
restState = rest;
}
Expand Down

0 comments on commit 592a01f

Please sign in to comment.