Giter Site home page Giter Site logo

redux-offline / redux-offline Goto Github PK

View Code? Open in Web Editor NEW
6.1K 6.1K 396.0 5.5 MB

Build Offline-First Apps for Web and React Native

Home Page: https://redux-offline.github.io/redux-offline/

License: MIT License

JavaScript 100.00%
hacktoberfest offline-capable react react-native redux

redux-offline's Introduction

redux-offline

npm version travis

🚨🚨🚨🚨 Maintainers wanted


Persistent Redux store for Reasonaboutable™️ Offline-First applications, with first-class support for optimistic UI. Use with React, React Native, or as standalone state container for any web app.

Redux Offline is now being maintained by a community driven team. The new versions of the library will now be available under the npm organization @redux-offline. Big thank you to @jevakallio for creating this amazing library in the first place.

Quick start

1. Install with npm (or Yarn)
For React Native 0.60+
npm install --save @redux-offline/redux-offline@native

For React Native Expo SDK 36

npm install --save @redux-offline/redux-offline@expo

For React Native <= 0.59

npm install --save @redux-offline/redux-offline
2. Add the offline store enhancer with compose
import { applyMiddleware, createStore, compose } from 'redux';
import { offline } from '@redux-offline/redux-offline';
import offlineConfig from '@redux-offline/redux-offline/lib/defaults';

// ...

const store = createStore(
  reducer,
  preloadedState,
  compose(
    applyMiddleware(middleware),
    offline(offlineConfig)
  )
);
3. Decorate actions with offline metadata
const followUser = userId => ({
  type: 'FOLLOW_USER_REQUEST',
  payload: { userId },
  meta: {
    offline: {
      // the network action to execute:
      effect: { url: '/api/follow', method: 'POST', json: { userId } },
      // action to dispatch when effect succeeds:
      commit: { type: 'FOLLOW_USER_COMMIT', meta: { userId } },
      // action to dispatch if network action fails permanently:
      rollback: { type: 'FOLLOW_USER_ROLLBACK', meta: { userId } }
    }
  }
});

If the effect payload is something other than JSON you can pass the body and headers:

const registerUser = (name, email) => ({
  type: 'REGISTER_USER',
  payload: { name, email },
  meta: {
    offline: {
      // the network action to execute:
      effect: { url: '/api/register', method: 'POST', body: `name=${name}&email=${email}`, headers: { 'content-type': 'application/x-www-form-urlencoded' } },
      // action to dispatch when effect succeeds:
      commit: { type: 'REGISTER_USER_COMMIT', meta: { name, email } },
      // action to dispatch if network action fails permanently:
      rollback: { type: 'REGISTER_USER_ROLLBACK', meta: { name, email } }
    }
  }
});
4. (React Native Android) Ask permission to read network status

If writing a native app for Android, you'll need to make sure to request the permission to access network state in your AndroidManifest.xml:

  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

See Documentation for configuration options, the full API, and common recipes.

Contributing

Improvements and additions welcome. For large changes, please submit a discussion issue before jumping to coding; we'd hate you to waste the effort.

If you are reporting a bug, please include code that reproduces the error. Here is a starting application on CodeSandbox.

In lieu of a formal style guide, follow the included eslint rules, and use Prettier to format your code.

Miscellanea

Usage with Redux Persist v5

In case you want to use a custom redux-persist version, there is an example configuration.

Prior art

Redux Offline is a distillation of patterns discovered while building apps using previously existing libraries:

Without their work, Redux Offline wouldn't exist. If you like the ideas behind Redux Offline, but want to build your own stack from lower-level components, these are good places to start.

License

MIT

redux-offline's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redux-offline's Issues

What about merge / conflict scenarios?

Given an backend with multiple client applications - for example a mobile and a web client. The client updates some data using the mobile application while being offline. Then the user makes some data changes using the web client, which immediately saves the changes to the backend. Now, when the mobile application gets network connectivity again, we technically have a conflict.

How do we choose which data of the outbox queue should be sent to the server and which should be discarded in favor of the newer remote data? Or is this more kind of a backend topic? I haven't dealt with such scenarios yet, but a possible solution which comes to mind is to integrate a timestamp into the request and let the backend decide, if it should accept the changes or not.

Pending queue gets rewritten on rehydration

First of all thanks for such a useful library!

I am having a little problem with actions that get fired before the rehydrate action.

Context:

  • I have a request that gets fired before persist/REHYDRATE action. It gets added to the pending queue
  • persist/REHYDRATEgets fired and rehydrates app state, wiping out the pending queue

Problem:

  • The request stays in oblivion for the rest of eternity 🎇

Proposed solution:

  • I solved this by blacklisting offline in my persistOptions. But maybe we should blacklist offline in redux-offline persistOptions.
persistOptions: { blacklist: ["offline"] }

With that said, I don't know if blacklisting offline in redux-persist will have any negative consequences. If that's the case, does anybody have any other solution?

Decaying schedule question

The question is, when the requests are retried and my data status become to "Unregistered (off)", and after returns to "Home", Should not reset the decaying schedule to the beggining instead of continue from the last step where it was (for example, "After 1 minute")?

Thanks!

Commit / rollback never called

I am working on React native app , I am trying to use redux-offline lib in my project.

When I tried to add user from offline but none of actions INSERT_NEW_USER_OFFLINE / INSERT_NEW_USER_FAILURE called, i.e commit or rollback is not calling.It always its call INSERT_NEW_USER

insertNewUserData: (userData) => {
const effectReconciler = () => {
return (api.setUsers({
user_id: userData.id,
user_first_name: userData.firstName,
user_last_name: userData.lastName,
updated_at: Math.floor(Date.now() / 1000)
}));
};
const commit = () => {
return { type: 'INSERT_NEW_USER_OFFLINE', payload: userData, meta: { userData } };
};
const rollback = () => {
return { type: 'INSERT_NEW_USER_FAILURE', payload: userData, meta: { userData } };
};

return {
type: 'INSERT_NEW_USER',
payload: userData,
meta: {
offline: {
effect: effectReconciler(),
commit,
rollback
}
}
};

Let me know if anything need to change in above code..

Unexpected key "offline" found in previous state received by the reducer. Expected to find one of the known reducer keys instead: "posts", "comments", "routing". Unexpected keys will be ignored.

So, there appears to be am issue with combineReducers (See attached error image). My code is as follows:

index.js

import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux'; // we need this for react-router
import posts from './posts';
import comments from './comments';

const rootReducer = combineReducers({ posts, comments, routing: routerReducer });

export default rootReducer;

posts.js

function posts(state = [], action) {
  switch (action.type) {
    case 'FETCH_POSTS':
      return action.payload;
    case 'INCREMENT_LIKES' :
      const i = action.index;
      return state;
    default:
      return state;
  }
}

export default posts;

comments.js

function comments(state = {}, action) {
  if (action.type === 'FETCH_COMMENTS') {
    return action.payload;
  } else if (action.type === 'ADD_COMMENT') {
    return state;
  } else if (action.type === 'REMOVE_COMMENT') {
    return state;
  };
  return state;
}

export default comments;

actionCreators.js

// fetch posts
export function fetchPosts() {
  console.log("Load Posts has been fired");
  return dispatch => {
    Posts.on('value', posts => {
      dispatch({
        type: 'FETCH_POSTS',
        payload: posts.val(),
        meta: {
          offline: {
            effect: {},
            commit: { type: 'FETCH_POSTS_COMMIT', meta: { posts } },
            rollback: { type: 'FETCH_POSTS_ROLLBACK', meta: { posts } },
          }
        }
      });
    });
  };
}

// fetch comments
export function fetchComments() {
  console.log("Load Comments has been fired");
  return dispatch => {
    Comments.on('value', comments => {
      dispatch({
        type: 'FETCH_COMMENTS',
        payload: comments.val(),
        meta: {
          offline: {
            effect: {},
            commit: { type: 'FETCH_COMMENTS_COMMIT', meta: { comments } },
            rollback: { type: 'FETCH_COMMENT_ROLLBACK', meta: { comments } },
          }
        }
      });
    });
  };
}

export function increment(i) {
  let currentLikesVal = 0;

  // Retrieve the current value of likes
  Posts.child(i).child('likes').once("value", function(itemData) {
    currentLikesVal = itemData.val() + 1;
  });

  var updateData = {
    likes: currentLikesVal
  }

  return dispatch => {
    dispatch({
      type: 'INCREMENT_LIKES', index: 1,
      payload: Posts.child(i).update(updateData),
      meta: {
        offline: {
          effect: {},
          commit: { type: 'INCREMENT_LIKES_COMMIT', meta: { updateData } },
          rollback: { type: 'INCREMENT_LIKES_ROLLBACK', meta: { updateData } },
        }
      }
    });
  };
}

/*
  Comments
*/

export function addComment(postId, author, comment) {
  var postData = {
    text: comment,
    user: author
  };

  return dispatch => {
    dispatch({
      type: 'ADD_COMMENT', postId, author, comment,
      payload: Comments.child(postId).push(postData),
      meta: {
        offline: {
          effect: {},
          commit: { type: 'ADD_COMMENT_COMMIT', meta: { postData } },
          rollback: { type: 'ADD_COMMENT_ROLLBACK', meta: { postData } },
        }
      }
    });
  } 
}

export function removeComment(postId, i){
  console.log('i in removeCommet action is = ' + i)
  return dispatch => {
    dispatch({
      type: 'REMOVE_COMMENT', i, postId,
      payload: Comments.child(postId).child(i).remove(),
      meta: {
        offline: {
          effect: {},
          commit: { type: 'REMOVE_COMMENT_COMMIT', meta: { postId } },
          rollback: { type: 'REMOVE_COMMENT_ROLLBACK', meta: { postId } },
        }
      }
    });
  };
}

error

There is a way to call multiple dispatch in commit and rollback?

Previously i used callbacks for the request, and there was something like this, when the request was success.

if (isSuccess) {
    dispatch({ type: ITEMS_SUCCESS, isPaging: params.isPaging, categoryId: catId, data: response });
    if (key == 'x') {
        dispatch({ type: SET_PAGE_X, page: params.page + 1 }); //External Dispatch (to other reducer)
    } else {
        dispatch({ type: SET_PAGE_Y, page: params.page + 1 }); //External Dispatch (to other reducer)
    }
}

this library only accept one dispatch commit: Action. Exist a way for send multiples?

Pass Commit and Rollback actions to other middlewares

Hi!

I'm using both redux-offline & redux-logger and I first tought that redux-offline wasn't working because the Commit and Rollback actions were not logged by the redux-logger middleware (I really scratched my head on this one). In fact, those actions are not passed through the middleware chain as next is only called on the Request action.

What do you think about passing the actions down through the middleware chain ?

PROS :

  • Better integration with other middlewares
  • Better debugging experience

CONS :

  • Depending on the middlewares order not all will be called with those actions e.g if redux-logger is defined higher in the middleware chain than redux-offline it will not be called with the Commit and Rollback actions

Whether or not, I thiink it will be a good idea to add a note on that in the README to avoid other users to make the same mistake and to lose time as redux-logger is heavy used middleware.

I can make a PR if needed.

Adding tests

Hi there,

Really cool library! I was curious if you have any plans on adding tests in the future. I would love to be able to contribute and I generally find a good entry point for me into a new codebase is writing some tests, so if it's something you're interested in, I would be happy to start putting some together.

How to drop "older" actions?

Hi,

first of all: thank you for this library!

When reading through the docs I was wondering what happens in this scenario:

  1. User likes a post
  2. User dislikes a post

When this scenario happens while offline, obviously nothing should be send to the server.

How could one implement this in a smart way (if possible for now)?

offline(offlineConfig) inside compose cause: `Element type is invalid`

This happens when the application is installed and run a second time.

My imports:

import { applyMiddleware, createStore, compose } from 'redux';
import { offline } from 'redux-offline';
import offlineConfig from 'redux-offline/lib/defaults';
const store = createStore(
    rootReducer,
    compose(
      applyMiddleware(thunk),
      offline(offlineConfig) //Error here.. When i comment this line, the error not occur
    )
  );

INSIDE redux-offline/lib/defaults/index.js

exports.default = {
  rehydrate: true,
  persist: _persist2.default, //This line exactly cause the problem
  detectNetwork: _detectNetwork2.default,
  batch: _batch2.default,
  effect: _effect2.default,
  retry: _retry2.default,
  discard: _discard2.default
};

Complete Error:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of NavigationCard``

screen shot 2017-05-04 at 12 13 39 pm

Package.json info:

"react": "~15.3.2",
"react-native": "^0.33.0",

screen shot 2017-05-04 at 12 15 27 pm

react-native -v:

react-native-cli: 1.2.0
react-native: 0.33.1

UPDATE

I'm using NavigationExperimental in a react version 15.3.2 when still available, But apparently some of the redux-persist tries to read the code where I am using this and causes the error..

How to wait until commit has completed before dispatching additional actions?

Is there currently anyway to ensure that the requests in the effect handler are made in series? I have an api where I create multiple resource which depend on another. So I have to ensure to create one entity first before I can add a related resource.

Any ideas?

EDIT: Actually I would need a way to dispatch another action after the commit action of a previous has been processed. To clarify further: I have a resource /categories , when I add a category on the client, I optimistically generate a temporary id, which eventually will be updated after the commit action has been dispatched. With the new id, I add a product to /categories/:id/products <-- that is why I have to wait until the commit of the createCategory action has been dispatched. I need a valid id in order to create a product.

Store connection type "wifi" or "cell"

It would be helpful to know the connection type in redux offline when using react-native. This way certain heavy data transactions can be avoided, such as photo uploading.

NetInfo can be used to detect this status for both IOS and Android as "wifi" or "cell". The result could be stored as offline.netInfo.

I'd be happy to put in a PR ibce the details are all sorted.

(persist): discussion around improving peristence

Love the api so far,

One shortcoming to the createOfflineStore setup (higher order store creator? 🥇) is that it is not obvious how to achieve more complex persistence patterns. e.g. the most common one of storing some state in session storage and other in localstorage.

Off the top of my head here are a few related ideas:

  1. Consider having persist config be an array of configs, generate one persistor per config
  2. Consider reimplementing redux-persist
    • make config a nested structure, recurse down the tree persisting things according to their config
    • this could open up some interesting api options like colocating persistence config with each reducer
  3. single key persistence
    • serialize each slice of the state (time throttled a la redux-persist)
    • but do not write this state to disk immediately
    • wait until all queued updates are serialized and then write all serialized chunks into one storage key

3 is I think particularly exciting because with redux-offline the consistency of the stored data is paramount - and single state writes will be more likely to be consistent. Additionally it could unlock options like storing an incremental actions with periodic compaction.

Just thoughts for now...

Feature request: Undo functionality

I'm building a offline-first React Native app using redux-offline. Right now I'm trying to find the best way to provide undo functionality (if there's a good way to do this already please let me know). What I'm suggesting is something like this:

  1. A configurable delay before the first request is fired when online (let's say 10 seconds).
  2. Expose an undo action creator that (a) triggers an action for the redux-offline reducer to remove the most recent item in the outbox and (b) triggers the rollback action with the associated metadata of that item.
  3. Expose a selector that indicates whether an undo is possible (i.e. there's at least one item in the outbox that has not yet triggered a request to the server).

HowTo: Incorporate metatdata with thunk/firebase actions?

So I'm currently using redux/thunk/firebase, where action payloads are used to contact firebase. as follows:

export function addComment(postId, author, comment) {
  var postData = {
    text: comment,
    user: author
  };

  return dispatch => {
    dispatch({
      type: 'ADD_COMMENT', postId, author, comment,
      payload: Comments.child(postId).push(postData)
    });
  } 
}

Is there a best practice for decorating actions with offline metadata in the above example/How would you structure your payload/metada in the above example?

In addition to the above, my store.js is as follows:

import { applyMiddleware, compose } from 'redux';
import { createOfflineStore } from 'redux-offline';
import offlineConfig from 'redux-offline/lib/defaults';
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router'
import thunk from 'redux-thunk';
import { fetchPosts, fetchComments } from './actions/actionCreators';
import rootReducer from './reducers/index';

// Expose the redux store to the chrome/opera devTols extensions
const enhancers = compose(
  window.devToolsExtension ? window.devToolsExtension() : f => f
);

const store = createStore(
  rootReducer,
  enhancers,
  applyMiddleware(thunk),
  offlineConfig
);

// First (thunk) load of all comments and posts from firebase
store.dispatch(fetchPosts());
store.dispatch(fetchComments());

and actionCreators.js as follows:

// fetch posts
export function fetchPosts() {
  console.log("Load Posts has been fired");
  return dispatch => {
    Posts.on('value', posts => {
      dispatch({
        type: 'FETCH_POSTS',
        payload: posts.val()
      });
    });
  };
}

// fetch comments
export function fetchComments() {
  console.log("Load Comments has been fired");
  return dispatch => {
    Comments.on('value', comments => {
      dispatch({
        type: 'FETCH_COMMENTS',
        payload: comments.val()
      });
    });
  };
}

backend implications/thoughts?

Love the blog post and this library. Wondering if your lessons learned about offline-first include any implications for the server-side?

Any web servers or server architectures that you think would pair particularly well with redux-offline? Any REST API or fetch replacements you think are good compliments?

Feel free to just close this without responding if you don't have time (I know it's not really an issue but not sure the best forum for discussing the project).

When are actions added to the outbox queue?

Which determines whether an action is added to the outbox queue, the Promise result from meta.offline.effect or the network status from state.offline.online? If the latter when is the enqueue decision made, before the effectReconciler is fired?

redux-offline with angular2 and/or angular-redux/store?

hey, i am trying to get redux-offline running with angular2.

is there a recommended way to make a provider out of redux-offline.

i dont know if this is the right place here. do i have to ask this question here: https://github.com/angular-redux/store ?

are there any disadvantages of using redux-offline in its pure form vs angular-redux/store or can i combine them without problem?

if i see it clear, angular-redux uses redux under the hood just like redux-offline and adds some conviniet wrappers around it. so is it the only thing that has to be changed here

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

thx in advance for any hints on the subject. i really like to use redux-offline for offline-first projects but like to able to use the convinience off angluar-redux/store with using observables etc.

Can commit take the response from effect

I read on the readme that it's possible to pass data from the effect execution to the commit

Or sometimes, in order to render the final UI state, you need some data from the server response.

I wonder how that's possible. Is the Promise data somehow injected in the commit action?

Why not use enhancer API?

I noticed you're providing your own createStore function. Why not use export an enhancer instead? (Like Redux DevTools does.)

Question: Clearing outbox?

I can't seem to figure out how to clear the outbox. If a user logs out of the client I need to clear the outbox and any remaining requests. Is there anyway to easily do this? I have a root reducer that has a reset method where I reset the state of my whole store but the offline state doesn't reset.

Access to dispatch

Is it possible to add access to dispatch from EffectsReconciler?

-type EffectsReconciler = (effect: any, action: OfflineAction) => Promise<any>
+type EffectsReconciler = (effect: any, action: OfflineAction, { dispatch }) => Promise<any>

commit is fired multiple times if other actions are dispatched during request

Following scenario (react-native, should apply to web, too):

During component mounting, I dispatch an action to be picked up by redux-offline. During the time the request is made, I dispatch some other actions, which should not be handled by redux-offline. However, these actions will be picked up by the middleware and will end in calling the commit handler multiple times for the amount of times of the actions, which have been dispatched during the time the request is made (or during the time the effect waits for promise resolve).

I hope you understand what I mean, it's not that simple to describe. My proposed solution to this would be to check in the middleware if an meta.offline key is present, otherwise just ignore the action.

What do you think?

Better description in README

I was unsure of what exactly this library does even after glancing over readme and listening to your lightning talk.

I'd suggest adding a couple of sentences clarifying the following:

  • This library is not responsible for assets caching. You still have to use service workers and something like sw-precache

  • This library does more than persistence. While offline, it saves all actions to a queue and tries to synchronize them when you get back online. And more importantly, it proposes an architecture for doing so.

It's nice to understand what something is for before trying to understand "how to use it".

Uncaught TypeError: Cannot read property 'outbox' of undefined

I wasn't sure if this should be reported here or in the https://github.com/zalmoxisus/redux-devtools-extension repo. However, the stacktrace is directed at the middleware.js file from this repo...

When I use compose from redux, there is no error. But when I use composeWithDevTools from redux-devtools-extension, I get this error on the client:

middleware.js?1387:29 Uncaught TypeError: Cannot read property 'outbox' of undefined
    at take (eval at <anonymous> (vendor-cba7902….js:6401), <anonymous>:29:38)
    at Object.eval [as dispatch] (eval at <anonymous> (vendor-cba7902….js:6401), <anonymous>:68:23)
    at dispatch (<anonymous>:2:31397)
    at eval (eval at <anonymous> (vendor-cba7902….js:5803), <anonymous>:22:18)
    at eval (eval at <anonymous> (vendor-cba7902….js:1892), <anonymous>:41:20)
    at eval (eval at <anonymous> (vendor-cba7902….js:1899), <anonymous>:14:16)
    at Object.eval [as dispatch] (eval at <anonymous> (vendor-cba7902….js:6352), <anonymous>:68:22)
    at Object.dispatch (<anonymous>:2:1507)
    at handleLocationChange (eval at <anonymous> (vendor-cba7902….js:5810), <anonymous>:99:11)
    at syncHistoryWithStore (eval at <anonymous> (vendor-cba7902….js:5810), <anonymous>:108:5)
take	@	middleware.js?1387:29
(anonymous)	@	middleware.js?1387:68
dispatch	@	VM3243:2
(anonymous)	@	middleware.js?26f3:22
(anonymous)	@	index.js?3a63:41
(anonymous)	@	index.js?f248:14
(anonymous)	@	createEpicMiddleware.js?60f1:59
dispatch	@	VM3243:2
handleLocationChange	@	sync.js?dcab:99
syncHistoryWithStore	@	sync.js?dcab:108
(anonymous)	@	browser.index.js?1ce9:28
429	@	bundle-7125933….js:82
__webpack_require__	@	vendor-cba7902….js:55
1265	@	bundle-7125933….js:9
__webpack_require__	@	vendor-cba7902….js:55
webpackJsonpCallback	@	vendor-cba7902….js:26
(anonymous)	@	bundle-7125933….js:1

Which points to the following lines:

screen shot 2017-04-15 at 11 11 34 pm

Here is my redux/store.js:

// @flow
import { applyMiddleware, createStore } from 'redux'
import { createEpicMiddleware } from 'redux-observable'
import thunk from 'redux-thunk'
import promiseMiddleware from 'redux-promise-middleware'
import { routerMiddleware } from 'react-router-redux'
import { offline } from 'redux-offline'
import offlineConfig from 'redux-offline/lib/defaults'
import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction'
import rootEpic from './epics'
import rootReducer from './modules'


function configureStore (
  preloadedState: Object, history: Object,
): Object {

  const epicMiddleware = createEpicMiddleware(rootEpic)

  const middleware = [
    epicMiddleware,
    thunk,
    promiseMiddleware(),
    routerMiddleware(history),
  ]

  // https://github.com/zalmoxisus/redux-devtools-extension
  // https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f
  const enhancer = composeWithDevTools(
    applyMiddleware(...middleware),
    offline(offlineConfig)
  )

  const store = createStore(rootReducer, preloadedState, enhancer)

  return store

}


export default configureStore

I'm not sure how to proceed, but let me know if I can provide anything else!

How know if the app is offline or online

Basing on

The current reported online state is stored in your store state as boolean state.offline.online

I'm trying to read from my store this property without success:

console.log('this.props.myStore offline ===> ', this.props.myStore.offline); ==> That return undefined.

console.log('this.props.myStore offline ===> ', this.props.myStore.offline.online); ==> online from offline object cause crash

Is there something I'm doing wrong? I'm missing?

Passing combined reducer results in error / warning

Came around an issue, when passing a rootReducer which has been combined using combineReducer function from redux. Redux will raise a warning (or en error on react-native), stating that the key offline, which is added by the reducer enhancer function in updater.js is unexpected.

Well it's the intended behaviour of combinedReducer to warn if there is an unexpected key added. I'm just wondering how to get around this? I'm now using my own implementation of combineReducer to prevent the (in react-native very annoying) error.

image

How can I dispatch an action after commit?

I need to dispatch an action after commit action is dispatched but I cant find the way. I think there is currently no option.. It is possible to pass function instead object on commit attribute? Do anybody have any idea? Thanks!

Offline state slice not on root level

Hi,

I would really like to test out this library and in order to do so I need to have the offline state slice in another location than root. As far as I can see I can not control the state locations in the config and I have tried to make changes to the source code but have not yet succeeded in making it work.

Anyone here that managed to make this work?

Best regards,
Ariel

redux-offline-sauce

Hi there,

Cool library! I was working on redux-offline-sauce to provide utility function similar to what reduxsauce provided for redux. It would be great if you could give some opinions/comments about my small library.

Thanks.

Effect does not get triggered

I'm trying to fix this problem for few days, still couldn't find a fix, so I'm wondering if it's an issue with the library.

// package.json
    "firebase": "^3.7.6",
    "firebase-functions": "^0.5.5",
    "material-ui": "~0.16.4",
    "react": "~15.4.0",
    "react-dom": "~15.4.0",
    "react-redux": "^5.0.1",
    "react-tap-event-plugin": "~2.0.0",
    "redux-offline": "^2.0.0",
    "redux-saga": "~0.14.2"
// customConfig.js
import defaultConfig from 'redux-offline/lib/defaults';
import firebase from './firebase';

const db = firebase.database();

const firebaseAction = (type, { body, resource }) => {
  const { key, ...final } = body;
    switch (type) {
      case 'CREATE_FIREBASE':
        return db.ref(`${resource}/${key}`).set(final);
      case 'UPDATE_FIREBASE':
        return db.ref(`${resource}/${key}`).update(final);
      default:
        return null;
    }
}

export default {
  ...defaultConfig,
  effect: (effect, action) => {
    console.log('executing effect...')
    return firebaseAction(action.type, effect);
  }
}
import { offline } from 'redux-offline';
import offlineConfig from './customConfig';

// ...
    const sagaMiddleware = createSagaMiddleware();

    const store = createStore(reducer, undefined, compose(
        applyMiddleware(sagaMiddleware, routerMiddleware(hashHistory)),
        window.devToolsExtension ? window.devToolsExtension() : f => f,
      offline(offlineConfig)
    ));
    sagaMiddleware.run(saga);

screenshot from 2017-04-27 16 49 54

I also tried with default config but no luck, not even errors (it should throw errors as i don't pass method, URL params.

import offlineConfig from 'redux-offline/lib/defaults';

So far i thought i missed somewhere or configured wrongly, but seems like I followed just like in the docs. The persist works, even after refresh (after the network is disconnected), the data loads, seems the meta.offline thing is not being detected by the middleware?

Someone please help, if there is an example working project i could refer, it'd be helpful

Using redux-offline with Vue

Hello,

Is there a guide to use this library with Vue?

I read on the blog post that Vue is supported, but i can't find a mention or a guide for this on the docs.

I'm using Vue with Vuex (a state manager like Redux). It's possible to use this library with Vuex?

Thanks, and great work!!!

Use an Immutable store

It would be great if this library could work with Immutable.js stores, and I think it's a good idea to start talking about it in this issue.

I'm already using this library with an Immutable store and opened PR #54, but it only works with redux-offline v1.

To accept an Immutable store using my approach you only needed to set config.immutable = true. To achieve this, the only thing I needed was to teach redux-offline how to persist and rehydrate Immutable object. Luckily redux-persist-immutable makes this super easy, so I added it as a dependency.

Implementing this was straightforward: The key parts were using a different persistStore and autoRehydrate in index.js, and converting the user's state to javascript in middleware.js so that redux-offline internals can still work with plain JS objects.

Please let me know of better ways to tackle this problem. Supporting Immutable stores would be great!

Retry action?

Is there a way to get a action dispatched when it is retrying? I want to update my GUI to saying it is going to retry in X sec.

I see this logged to console:

'Retrying action', 'FETCH_ENTITY', 'with delay', 1000

Is there a way to make it dispatch an action at the same time? Like *_ROLLBACK and *_COMMIT?


This is an aside, an example of an end goal is:

  1. User takes a screenshot
  2. Clicks upload to Dropbox
  3. It starts the first upload attempt
  4. Now different things can happen
    • If this attempt finds it is offline, I want to show "You are currently offline, will upload when get online"
    • If it is succeeding, I want to show % uploaded
    • If it failed and will retry in X seconds I want to show countdown "Failed will retry in Xs"
    • If it is in "retrying" stages or "waiting for offline" I want to offer a button saying "Cancel upload and instead save to disk"

window is not defined

Hello! I have the following error when I'm using it with https://github.com/kriasoft/react-starter-kit . Maybe you need to check if window is not undefined or provide it via global variable?

ReferenceError: window is not defined

  - detectNetwork.js:21 Object.exports.default [as detectNetwork]
    [livery-client-react]/[redux-offline]/lib/defaults/detectNetwork.js:21:7

  - index.js:56 createOfflineStore
    [livery-client-react]/[redux-offline]/lib/index.js:56:12

Redux Offline without Redux

Hello,

Is it possible to use redux offline without redux?
I need to keep a rather huge data set inside my offline cache, so redux is not suitable for my application, as cloning the whole store every time a single data set is changed is not performant enough.
I like redux offline's network monitoring and action queue features though.
Can I use them without redux?
Thanks!

Commit and Rollback should be optional

There is a use-case in which I wish to POST something to the server, but am not concerned about acting on its success or failure (I do however want it to be added to outbox). In such an instance it should be possible to leave the commit and rollback fields undefined.

At the moment if commit is left undefined, the action continually attempts to resolve the 'effect'.

Retrying action ACTION with delay 1000
Retrying action ACTION with delay 5000
...

Basically the following should all be possible

const action = () => ({
    type: '...',
    payload: { ... },
    meta: {
        offline: true
    }
});
const action = () => ({
    type: '...',
    payload: { ... },
    meta: {
        offline: {
            effect: { ... }
        }
    }
});
const action = () => ({
    type: '...',
    payload: { ... },
    meta: {
        offline: {
            commit: { ... }
        }
    }
});
const action = () => ({
    type: '...',
    payload: { ... },
    meta: {
        offline: {
            rollback: { ... }
        }
    }
});

babel plugin dependencies when using react-native

Hey, first: thanks for this library, just came around this library because I have a project which will need proper offline handling. Was about to implement my own solution, but decided to give this one a try. Looks very promising!

However, when I add the dependency using yarn add redux-offline and import the createOfflineStoremethod, it seems that their is an issue with the babel plugins.

image

Any ideas how to fix this?

After update to 2.0, REHYDRATE or online status actions no longer fire

Basically I don't see any of the redux-offline actions firing after the upgrade to 2.0. Here's my store configuration:

export default function configureStore(initialState) {
  const debugware = (process.env.NODE_ENV === 'production') ? [] : [createLogger({ collapsed: true, predicate: () => ((process.env.NODE_ENV === 'development') || (process.env.NODE_ENV === 'test')) })]

  const store = createStore(
    rootReducer,
    initialState,
    (process.env.NODE_ENV === 'development') ?
      compose(
        applyMiddleware(thunkMiddleware, ...debugware),
        // window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f, // eslint-disable-line no-underscore-dangle
        offline({ ...offlineConfig, persistOptions: { storage: LocalForage } }),
      ) :
      compose(
        applyMiddleware(thunkMiddleware, ...debugware),
        offline({ ...offlineConfig, persistOptions: { storage: LocalForage } }),
      ),
  )

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept(['./common/modules', './admin/modules', './attendee/modules'], () => { store.replaceReducer(rootReducer) })
  }

  return store
}

Why commit lib folder?

Why do you commit the lib folder to git? You can have a prepublish step that generates it, and includes it in the files you push to npm, like many other libraries do.

Let me know if you need help with this.

Sagas

How does this play withe redux-sagas? I use sagas instead of redux thug and I wonder how to add it to redux-offline. Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.