npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-action-retry

v0.2.1

Published

At least once delivery of Redux Actions

Downloads

14

Readme

Installation

yarn add redux-action-retry

Getting Started

Basic Configuration

import uuid from 'uuid/v4';
import { REDUX_ACTION_RETRY } from 'redux-action-retry';

// actions to retry
export const SEND_LOGS_TO_SERVER = 'SEND_LOGS_TO_SERVER';
export const NOTIFY_ACTION = 'NOTIFY_ACTION';

// action creators
export function sendLogsToServerActionCreator() {
  return {
    type: SEND_LOGS_TO_SERVER,
    // meta must be config
    meta: {
      [REDUX_ACTION_RETRY]: {
        // the id will be used to identify the action in the cache.
        id: uuid()
      }
    }
  }
}
import {
  createStore,
  combineReducers,
  applyMiddleware,
} from 'redux';

import { createRetryMechanism } from 'redux-action-retry';
import { SEND_LOGS_TO_SERVER, NOTIFY_ACTION } from './actions';

const { reducer, reduxActionRetryMiddlewares, stateKeyName } = createRetryMechanism({
  cache: {
    // action types are keys
    [SEND_LOGS_TO_SERVER]: {
      type: SEND_LOGS_TO_SERVER,
    },
    [NOTIFY_ACTION]: {
      type: NOTIFY_ACTION,
    },
  },
});

export const store = createStore(
  combineReducers({
    // Use the stateKeyName as the key name of the reducer, as needed for the retry mechanism middlewares.
    [stateKeyName]: reducer
  }),
  applyMiddleware
    (
      // Spread the middlewares
      ...reduxActionRetryMiddlewares,
    )
);

Remove action from cache

When actions are dispatched, the middleware immediately caches the action, if the action is successful you can remove it dispatching a remove action.

import { put } from 'redux-saga/effects'
import { removeActionCreator } from 'redux-action-retry';

function* sendLogsToServer(action) {
  // domain logic...
  // ...

  // if all good then remove from retry mechanism
  yield put(removeActionCreator(action))
}

Retry actions

When retrying is needed you can dispatch a retry all action.

import { put } from 'redux-saga/effects'
import { retryAllActionCreator } from 'redux-action-retry';

function* appForeground() {
  // domain logic...
  // ...

  // retry all cached actions
  yield put(retryAllActionCreator())
}

Reset Store

import {
  resetActionCreator,
} from 'redux-action-retry';

function logout() {
  // domain logic...
  // ...

  // reset the store for redux-action-retry
  dispatch(resetActionCreator())
}

Advanced Usage

Cooldown

Without cooldown timeline Without cooldown timeline

With cooldown timeline With cooldown timeline

The amount of time an action could take executing (Flying time) might be indicated, for example, by the timeout of a request, if a request is bound to fail after 30 seconds by timeout, then the action could be flying for at least 30 seconds.

Often when actions are dispatched, we wouldn't want them to be retried before we know they've succeeded or failed.

Configuration

export const REQUEST_TIMEOUT = duration('PT30S');
import {
  createStore,
  combineReducers,
  applyMiddleware,
} from 'redux';
import { duration } from "moment";
import { createRetryMechanism, Cooldown } from 'redux-action-retry';
import { SEND_LOGS_TO_SERVER, NOTIFY_ACTION } from './actions';

const { reducer, reduxActionRetryMiddlewares, stateKeyName } = createRetryMechanism({
  cache: {
    [SEND_LOGS_TO_SERVER]: {
      type: SEND_LOGS_TO_SERVER,
      // cooldown config is per action
      cooldownTime: duration('PT31S'),
    },
    [NOTIFY_ACTION]: {
      type: NOTIFY_ACTION,
      // As a rule of thumb add a bit more of time to allow for actual code and framework delays.
      cooldownTime: duration('PT31S'),
    },
    extensions: [
      Cooldown,
    ]
  },
});

export const store = createStore(
  combineReducers({
    [stateKeyName]: reducer
  }),
  applyMiddleware
    (
      ...reduxActionRetryMiddlewares,
    )
);

Early response

Cancel cooldown timeline Cancel cooldown timeline

In the case of an early fail response we might want the action be to be retryable sooner than the cooldown time, so we might want to cancel it.

import { put } from 'redux-saga/effects'
import { cancelCooldownActionCreator } from 'redux-action-retry';

function* sendLogsToServer(action) {
  // domain logic...
  // ...

  if(allGood) {
    yield put(removeActionCreator(action))
  } else {
    // in case of early fail
    yield put(cancelCooldownActionCreator(action))
  }
}

Cool and Retry All utility

In case we want to Retry All without caring for the cooldown time, dispatch a coolAndRetryAllActionCreator.

import { coolAndRetryAllActionCreator } from 'redux-action-retry';

function* rehydrate(action) {
  // domain logic...
  // ...

  yield put(coolAndRetryAllActionCreator(action))
}

Times

State without times:

{"REDUX_ACTION_RETRY": {
        "cache": [{
                "action": {
                    "type": "SEND_LOGS_TO_SERVER",
                    "meta": {
                        "REDUX_ACTION_RETRY": {
                            "id": "3b2a7b5f-47f8-4774-af84-d28c5ecb61a7"
                        }
                    }
                },
            }]}}

State with times:

{ "REDUX_ACTION_RETRY": {
        "cache": [{
                "action": {
                    "type": "SEND_LOGS_TO_SERVER",
                    "meta": {
                        "REDUX_ACTION_RETRY": {
                            "id": "3b2a7b5f-47f8-4774-af84-d28c5ecb61a7"
                        }
                    }
                },
                "times": 5
            }]}}

In case you want to add a counter of the times an action have been retried, use the Times Extension.

Configuration

import {
  createStore,
  combineReducers,
  applyMiddleware,
} from 'redux';
import { duration } from "moment";
import { createRetryMechanism, times } from 'redux-action-retry';
import { SEND_LOGS_TO_SERVER, NOTIFY_ACTION } from './actions';

const { reducer, reduxActionRetryMiddlewares, stateKeyName } = createRetryMechanism({
  cache: {
    [SEND_LOGS_TO_SERVER]: {
      type: SEND_LOGS_TO_SERVER,
    },
    [NOTIFY_ACTION]: {
      type: NOTIFY_ACTION,
    },
    extensions: [
      times,
    ]
  },
});

export const store = createStore(
  combineReducers({
    [stateKeyName]: reducer
  }),
  applyMiddleware
    (
      ...reduxActionRetryMiddlewares,
    )
);

Use cases

Debugging / reports

In case of debugging or reading reports, having a count of how many times an action actually failed is very useful.

Optimistic response

We might want to keep triggering one saga but not the reducer, in those cases we could add a middleware to consume the action based on the times counter.

Cease retrying

In case we want to cease retrying after a number of attempts we could add a middleware that dispatches a remove command based on the times counter.

Time to live

In case we want to cease retrying after an amount of time, we could use Time to live extension.

Dead actions are prevented from retrying, but when to remove those from the cache is up to the user (might want to send for stats/reports).

Configuration

import {
  createStore,
  combineReducers,
  applyMiddleware,
} from 'redux';
import { duration } from "moment";
import { createRetryMechanism, TimeToLive } from 'redux-action-retry';
import { SEND_LOGS_TO_SERVER, NOTIFY_ACTION } from './actions';

const { reducer, reduxActionRetryMiddlewares, stateKeyName } = createRetryMechanism({
  cache: {
    [SEND_LOGS_TO_SERVER]: {
      type: SEND_LOGS_TO_SERVER,
      // time to live config is per action
      timeToLive: duration('P3D')
    },
    [NOTIFY_ACTION]: {
      type: NOTIFY_ACTION,
      cooldownTime: duration('P1D'),
    },
    extensions: [
      TimeToLive,
    ]
  },
});

export const store = createStore(
  combineReducers({
    [stateKeyName]: reducer
  }),
  applyMiddleware
    (
      ...reduxActionRetryMiddlewares,
    )
);

How to remove dead actions

When we want to remove dead actions we dispatch a collectGarbageActionCreator.

import { collectGarbageActionCreator } from 'redux-action-retry';

function* rehydrate(action) {
  // domain logic...
  // ...

  yield put(collectGarbageActionCreator(action))
  // we might want to collect garbage before issuing a retry all
  yield put(coolAndRetryAllActionCreator(action))
}