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

Make library work with Immutable.js stores #54

Closed
wants to merge 4 commits into from
Closed
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
1 change: 0 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"react"
],
"plugins": [
"flow-react-proptypes",
"transform-object-rest-spread",
"transform-class-properties",
"transform-flow-strip-types"
Expand Down
62 changes: 34 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,31 @@ Redux Offline is very, very new. If you find a bug, good job for being an early
npm install --save redux-offline
```

##### 2. Replace [redux createStore](http://redux.js.org/docs/api/createStore.html) with createOfflineStore
##### 2. Add the `offline` [store enhancer](http://redux.js.org/docs/Glossary.html#store-enhancer) with `compose`
```diff

- import { applyMiddleware, createStore } from 'redux';
+ import { applyMiddleware } from 'redux';
+ import { createOfflineStore } from 'redux-offline';
+ import { applyMiddleware, createStore, compose } from 'redux';
+ import { offline } from 'redux-offline';
+ import offlineConfig from 'redux-offline/lib/defaults';

// ...

- const store = createStore(
+ const store = createOfflineStore(
const store = createStore(
reducer,
preloadedState,
applyMiddleware(middleware),
+ offlineConfig
- applyMiddleware(middleware)
+ compose(
+ applyMiddleware(middleware),
+ offline(offlineConfig)
+ )
);
```

See [Configuration](#configuration) for overriding default configurations.

Looking for `createOfflineStore` from redux-offline 1.x? See migration instructions in the [2.0.0 release notes](https://github.com/jevakallio/redux-offline/releases/tag/v2.0.0).

##### 3. Decorate actions with offline metadata

```js
Expand Down Expand Up @@ -208,7 +212,7 @@ const ordersReducer = (state, action) {

The last part of the offline metadata is `meta.offline.effect`. This property can contain anything, and will be passed as-is to the effects reconciler.

The **effects reconciler** is a function that you pass to `createOfflineStore` configuration, whose responsibility it is to take the effect payload, send it over the network, and return a Promise that resolves if sending was successful or rejects if the sending failed. The method is passed the full action as a second parameter:
The **effects reconciler** is a function that you pass to offline enhancer configuration, whose responsibility it is to take the effect payload, send it over the network, and return a Promise that resolves if sending was successful or rejects if the sending failed. The method is passed the full action as a second parameter:

```js
type EffectsReconciler = (effect: any, action: OfflineAction) => Promise<any>
Expand Down Expand Up @@ -300,45 +304,44 @@ export type Config = {
};
```

#### Passing configuration to createOfflineStore
The `createOfflineStore` store creator takes the [configuration object](#configuration-object) as a final parameter:
#### Passing configuration to the enhancer
The `offline` store enhancer takes the [configuration object](#configuration-object) as a final parameter:
```diff
+ import { createOfflineStore } from 'redux-offline';
+ import { offline } from 'redux-offline';
+ import defaultConfig from 'redux-offline/lib/defaults';
+
- const store = createStore(
+ const store = createOfflineStore(

const store = createStore(
reducer,
preloadedState,
middleware,
+ defaultConfig
- middleware
+ compose(middleware, offline(defaultConfig))
);
```

#### Overriding default properties
You can override any individual property in the default configuration:
```diff
import { createOfflineStore } from 'redux-offline';
import { offline } from 'redux-offline';
import defaultConfig from 'redux-offline/lib/defaults';

const customConfig = {
...defaultConfig,
effect: (effect, _action) => Api.send(effect)
}

const store = createOfflineStore(
const store = createStore(
reducer,
preloadedState,
middleware,
+ customConfig
- middleware
+ compose(middleware, offline(customConfig))
);
```

#### Only import what you need
The reason for default config is defined as a separate import is, that it pulls in the [redux-persist](https://github.com/rt2zz/redux-persist) dependency and a limited, but non-negligible amount of library code. If you want to minimize your bundle size, you'll want to avoid importing any code you don't use, and bring in only the pieces you need:

```diff
import { createOfflineStore } from 'redux-offline';
import { offline } from 'redux-offline';
import batch from 'redux-offline/lib/defaults/batch';
import retry from 'redux-offline/lib/defaults/retry';
import discard from 'redux-offline/lib/defaults/discard';
Expand All @@ -351,11 +354,12 @@ const myConfig = {
persist: (store) => MyCustomPersistence.persist(store)
};

const store = createOfflineStore(
const store = createStore(
reducer,
preloadedState,
middleware,
+ myConfig
- middleware
+ compose(middleware, offline(myConfig))
myConfig
);
```

Expand Down Expand Up @@ -462,10 +466,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
3 changes: 0 additions & 3 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ var _defaults2 = _interopRequireDefault(_defaults);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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


var applyDefaults = exports.applyDefaults = function applyDefaults() {
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

Expand Down
3 changes: 2 additions & 1 deletion lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ var OFFLINE_STATUS_CHANGED = exports.OFFLINE_STATUS_CHANGED = 'Offline/STATUS_CH
var OFFLINE_SCHEDULE_RETRY = exports.OFFLINE_SCHEDULE_RETRY = 'Offline/SCHEDULE_RETRY';
var OFFLINE_COMPLETE_RETRY = exports.OFFLINE_COMPLETE_RETRY = 'Offline/COMPLETE_RETRY';
var OFFLINE_SEND = exports.OFFLINE_SEND = 'Offline/SEND';
var OFFLINE_BUSY = exports.OFFLINE_BUSY = 'Offline/BUSY';
var OFFLINE_BUSY = exports.OFFLINE_BUSY = 'Offline/BUSY';
var PERSIST_REHYDRATE = exports.PERSIST_REHYDRATE = 'persist/REHYDRATE';
2 changes: 0 additions & 2 deletions lib/defaults/batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ Object.defineProperty(exports, "__esModule", {
value: true
});

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

exports.default = function (outbox) {
if (outbox.length > 0) {
return [outbox[0]];
Expand Down
2 changes: 1 addition & 1 deletion lib/defaults/detectNetwork.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ exports.default = function (callback) {
});
handle(callback, window.navigator.onLine);
}
};
};
2 changes: 0 additions & 2 deletions lib/defaults/discard.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ Object.defineProperty(exports, "__esModule", {

var _effect = require('./effect');

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

exports.default = function (error, action) {
var _retries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;

Expand Down
21 changes: 16 additions & 5 deletions lib/defaults/effect.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,33 @@ exports.NetworkError = NetworkError;

function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

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

function NetworkError(response, status) {
this.name = 'NetworkError';
this.status = status;
this.response = response;
}

//$FlowFixMe

/*global fetch*/

NetworkError.prototype = Error.prototype;
NetworkError.prototype.status = null;

var tryParseJSON = function tryParseJSON(json) {
if (!json) {
return null;
}
try {
return JSON.parse(json);
} catch (e) {
throw new Error('Failed to parse unexpected JSON response: ' + json);
}
};

var getResponseBody = function getResponseBody(res) {
var contentType = res.headers.get('content-type');
return contentType.indexOf('json') >= 0 ? res.json() : res.text();
return contentType.indexOf('json') >= 0 ? res.text().then(tryParseJSON) : res.text();
};

exports.default = function (effect, _action) {
Expand All @@ -38,7 +49,7 @@ exports.default = function (effect, _action) {
return getResponseBody(res);
} else {
return getResponseBody(res).then(function (body) {
throw new NetworkError(body, res.status);
throw new NetworkError(body || '', res.status);
});
}
});
Expand Down
1 change: 0 additions & 1 deletion lib/defaults/retry.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
value: true
});

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

var decaySchedule = [1000, //After 1 seconds
1000 * 5, //After 5 seconds
Expand Down
17 changes: 0 additions & 17 deletions lib/defaults/send.js

This file was deleted.

76 changes: 0 additions & 76 deletions lib/effects.js

This file was deleted.

1 change: 0 additions & 1 deletion lib/impl.js

This file was deleted.

18 changes: 12 additions & 6 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 @@ -17,14 +19,13 @@ var _config = require('./config');

var _actions = require('./actions');

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


// @TODO: Take createStore as config?

// eslint-disable-next-line no-unused-vars

/*global $Shape*/
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 +37,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;
config.persist = _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
Loading