0.2.0 • Published 5 years ago

redux-offline-wizard v0.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
5 years ago

Redux Offline Wizard

npm license

Provides fllowing features for redux apps :

  • Persistent redux store with support for store migrations out of the box : redux store will be saved in browser storage using localforage
  • Guaranteed scheduled or delayed XHR requests : setup requests to be sent for example after two days, smoe seconds, when user reopens app, etc
  • Auto-Retry for XHR requests: setup requests to be resent automatically. You can add maximum retry and expire timestamp to control retries
  • Support for optimistic UI: feel free to use optimism in your reducers, that means you can for example delete a post in UI as soon as user clicks trash button and call addToQueue (with retry option) to add delete request to queue. redux-offline-wizard will resend request (when goes online) until receive success response from server

Demo

A very usefull demo to play and see all redux-offlie-wizard does step by step and in action.
You can dispatch any action and see how store is persisted, requests are retried, scheduled requests are sent on time and more

Quick Start

1. Install the package

npm install --save redux-offline-wizard

2. Add reduxOfflineWizard branch in root of state tree

import { combineReducers } from "redux";
import { reduxOfflineWizard } from "redux-offline-wizard";

// ...

export default combineReducers({
  reduxOfflineWizard,
  // other branches ...
});

3. Use createOfflineStore to create store

import createOfflineStore from "redux-offline-wizard";

//...

export default createOfflineStore(
  reducers, 	// root reducer
  middleware, 	// middleware = applyMiddleware( ... );   
);

4. Pass renderer function to offline store creator

import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux";

//...

// createStore is the exported function in previous step
import createStore from "./store";	
// App Layout
import Layout from "./Layout";		

// put render in a function and pass it to 
// store creator, this function will receive
// the store as first argument
function renderApp(store) {			
  render(
    <Provider store={store}>
      <Layout />
    </Provider>,
    document.getElementById("root")
  );
}

// second argument is an int indicating the store version
createStore(renderApp, 0);	

5. Use addToQueue for XHR requests in actions

Now you can use addToQueue function to add XHR requests to outgoing or scheduled queue
More examples for addToQueue
Full addToQueue API

import { addToQueue } from "redux-offline-wizard";

// ...

// addToQueue(action, request, config)
export const someAction = () => addToQueue(
  {
    type: [ FETCH_GITHUB ],
    payload: {},
  },
  {
    url: "https://github.com",
    key: "https://github.com",
    body: {},
    method: "GET",
  },
  {
  	retry: true,
  	maxRetry: 3,
  }
)

6. Use fetch postfixes in reducers

To catch actions in reducers, you should use data fetch postfixes appended to action names

import { 
  DATA_FETCH_REJECTED,
  DATA_FETCH_PENDING,
  DATA_FETCH_FULFILLED,
} from "redux-offline-wizard";

import { FETCH_DATA } from "./actionNames"

const initialState = {
  pending: false,
  rejected: false,
  fulfilled: false,
  data: {}
};

export default (
  state = initialState, 
  action
) => {
  switch (action.type) {
    case `${FETCH_DATA}${DATA_FETCH_PENDING}`: {
      return {
      	...state,
		pending: true,
    	rejected: false,
  	  }
    }
    case `${FETCH_DATA}${DATA_FETCH_FULFILLED}`: {
      return {
      	...state,
		pending: false,
    	rejected: false,
    	fulfilled: true,
  	  }
    }
    case `${FETCH_DATA}${DATA_FETCH_REJECTED}`: {
      return {
      	...state,
		pending: false,
    	rejected: true,
  	  }
    }
    default: {
      return state;
    }
  }
}

Examples

Request with auto-retry

import { addToQueue } from "redux-offline-wizard";

import { FETCH_USER } from "./actionNames";

// ...

// addToQueue(action, request, config)
export const someAction = () => addToQueue(
  {
	// action names to dispatch    
	type: [ FETCH_USER ], 				
	// this payload will become meta (action.meta) 
	// in reducer, request's response will be main paylaod   
	payload: {},
  },
  {
    url: "https://user.fetch.api.url",
    key: "https://user.fetch.api.url",
    body: {},
    headers: {
		Authorization: `Bearer ${token}`,
		//...
	},
    method: "GET",
  },
  {
  	// if false, request will not be resent
  	retry: true,	
  	// request will be sent 4 times maximum					
  	maxRetry: 3,
  	// request have 60 seconds maximum time to get succeeded,
  	// otherwise will be considered failed and removed from queue						
  	expire: 60,							
  }
)

Delayed or scheduled requests

import { addToQueue } from "redux-offline-wizard";

import { REMIND_USER } from "./actionNames";

// ...

// addToQueue(action, request, config)
export const someAction = () => addToQueue(
  {
	// action names to dispatch    
	type: [ REMIND_USER ], 				
	// this payload will become meta (action.meta) 
	// in reducer, request's response will be main paylaod   
	payload: {},
  },
  {
    url: "https://remind.user.api.url",
    key: "https://remind.user.api.url",
    body: {
		reminder: 'e.g. Send Email to notify order expiration',
		//...
	},
	headers: {
		Authorization: `Bearer ${token}`,
		//...
	},
    method: "POST",
  },
  {
  	delay: {
		// if true, request will be added to outgoing queue on app start
		onEnter: false,  
		// request will be added to outgoing queue after at least one week
		timeout: 60 * 60 * 24 * 7,
  	}
  }
)

Full API

addToQueue(action, request , config)

addToQueue (
  {
    type: [ 
    	ACTION_NAME_1, 
    	ACTION_NAME_2,     	
    ],
    payload: {},
  },
  {
    url: "https://...",
    key: "some unique key",
    body: {},
    headers: {},
    method: "GET, POST, PUT, ...",
  },
  {
	retry: true,
	maxRetry: 3,
	expire: 60,
	delay: {
		onEnter: true,
		timeout: 60 * 60 * 24 * 7,
    },
	dataFetchPostfixes: {
	 	pending: 'pending action name postfix',
	 	rejected: 'rejected action name postfix',
		fulfilled: 'fulfilled action name postfix'
	}
  }
)
  • action type: an array including all action types to dispatch. All types will go through 2 out of these 3 states: pending , rejected , fulfilled. Notice: all action types supplied for type will get a postfix appended to end of them for any state. for example if action name is FETCH_USER, it will dispatch FETCH_DATA_PENDING when pending and FETCH_DATA_FULFILLED when fulfilled. Postfixes can be imported using named imports (see reducer example) or can be configured using config argument payload: action payload to be passed to reducer. Notice: reducers will receive this payload in meta field and instead will receive response of request in payload field of action
  • request url: request target key: a unique key for request Notice: avoid using a UUIDs for requests. a good practice is to use url as key but consider this way only one request with a url will exist in queue at a time and if you add any request with same url, will override existing one. However this is useful in many cases but if some requests need to have several instances, you can append some ID to url for use as key method: request method body : Request body. Can be any value valid for axios * headers (optional): Request headers
  • config (optional) retry: Default is false. if true, request will be retried until reach one of three conditions: request succeeds max retry reaches expiration timeout passes maxRetry: Maximum times to resend request. default is 5. for example a request with maxRetry: 2 will get maximum 3 tries expire: Expiration timeout in seconds. If this field was present and timeout reached, request will be dropped from queue and will not be retried delay onEnter: if true, Request will be added to outgoing queue when user visits app. default is false timeout: Time in seconds to wait before sending request. If present and timeout reached, request will be added to outgoing queue. For example if you want a request to be sent after at least one week from now, you can set `timeout: 3600 24 7`. Consider if onEnter is true, request will be sent on app start regardless of timeout dataFetchPostfixes: Postfixes to be appended to action types on any of 3 states. You can access default postfixes with named imports :
	import { 
	  DATA_FETCH_REJECTED,
	  DATA_FETCH_PENDING,
	  DATA_FETCH_FULFILLED,
	} from "redux-offline-wizard";

Contributing

Contributions of any scale are welcome

License

MIT

0.1.0

5 years ago

0.1.2

5 years ago

0.2.0

5 years ago

0.1.1

5 years ago

0.1.4

5 years ago

0.1.3

5 years ago

0.1.5

5 years ago