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-waiter

v0.5.0

Published

A promise lifecycle manager with redux

Downloads

253

Readme

Redux Waiter

NPM Build Status

Installation

npm i redux-waiter --save

This gives you access to the reducer, constants, actions, and selectors available.

Add to your combineReducers()

import { reducer } from 'redux-waiter';

const initialState = combineReducers({
  waiter: reducer,
});

const store = createStore(
  initialState,
  {},
  compose(applyMiddleware(...[thunk]))
);

NOTE: Ensure you have redux-thunk configured as middleware in your store implementation

Waiter model description and defaults

Each waiter initialized will have these properties.

const model = {
  // incremented after each request
  id: 0,

  // your waiter's name
  name: null,

  // your request to track
  requestCreator: null,

  // the params for your request to use
  params: null,

  // the promise returned from the requestCreator
  request: null,

  // the result of the promise
  response: null,

  // the error if returned from the promise
  error: null,

  // true if the request is called a second time and isPending
  isRefreshing: false,

  // true when the request is pending
  isPending: false,

  // true when the request returns an error
  isRejected: false,

  // true when the request returns successfully
  isResolved: false,

  // true if the request resolves or rejects
  isCompleted: false,

  // true if the request is canceled
  isCanceled: false,

  // true if the request rejected, and is being called again
  isRetrying: false,

  // start time of request in milliseconds UTC
  startTime: null,

  // end time of request in milliseconds UTC
  endTime: null,

  // difference in milliseconds of start and end times
  elapsedTime: null,

  // last time the model changed in milliseconds UTC
  lastModified: null,

  // how many times the request has been called and returned an error
  // resets after a successful response
  attempts: 0,
};

connectWaiter

import { connectWaiter } from 'redux-waiter'

connectWaiter is a higher-order-component that connects your waiter promise to another component. You can listen in on waiter events and dispatch other actions. You can add custom views to different waiter states, as well as add custom actions to the mount and unmount lifecycle events

Below is the full interface for connectWaiter, ordered in the sequence that the actions take place

import { connectWaiter } from 'redux-waiter';
import MyComponent from 'path/to/MyComponent';
import notification from 'path/to/notification';

const SearchRequestForm = connectWaiter({
  /* 
   * Configuration settings 
   */

  // name can be a string or a function with access to props 
  name: (props) => 'my-waiter-name',

  // requestCreator, your promise builder 
  requestCreator: (params, props) => yourAPI.getSomething(params),

  /* 
   * Alternate views 
   */

  pendingView: LoadingView,
  rejectedView: FailureView,

  /* 
   * Lifecycle configs and state change callbacks
   */

  // onMount
  onMount: (waiter, props) => {
    // Hey, we mounted!
    console.log('onMount', waiter)
  },

  // clear the waiter data when the component is added to the view
  clearOnMount: true,

  // create your promise request when mounting your component
  requestOnMount: true,

  // pass parameters to the request creator based on props
  requestOnMountParams: (props) => ({ name: 'First', last: 'Last' }),

  // like requestOnMountParams, but used to initialize the call to
  // the waiter again on props change
  requestOnPropsChange: (props) => ({ name: 'First', last: 'Last' }),

  // state change callbacks 
  onPending: (waiter, props) => {
    // And we're off! 
    console.log('onPending - ', waiter);
  },
  onResolve: (waiter, props) => {
    // Success!
    console.log('onResolve - ', waiter.response);
  },
  onReject: (waiter, props) => {
    // Oh no...error
    console.log('onReject - ', waiter.error);
  },
  onComplete: (waiter, props) => {
    // All done!
    console.log('onComplete - ', waiter);
  },
  onRefresh: (waiter, props) => {
    // We're updating our response!
    console.log('onRefresh - ', waiter)
  },
  onCancel: (waiter, props) => {
    // The waiter canceled, do what you need to clean up 
    console.log('onCancel - ', waiter)
  },

  // on unmount
  onUnmount: (waiter, props) => {
    // Say goodbye to your view! 
    console.log('onUnmount - ', waiter);
  },

  // clear the waiter data when the component is removed from view
  clearOnUnmount: true,

})(MyComponent);

Actions

callWaiter(waiterName, { params, requestCreator })

The call action will invoke the requestCreator with the supplied params and store all waiter processes to the waiterName given.

import { callWaiter } from 'redux-waiter';

dispatch(
  callWaiter('get-toy', {
    requestCreator: (params) => getToyPromise(params.id),
    params: { id: '1' },
  })
);

prepareWaiter(waiterName, { params, requestCreator })

Prepare is the same as callWaiter, but it will only store up the params and request creator to the waiterName. It will not invoke the requestCreator until callWaiter(waiterName) is dispatched

import { prepareWaiter } from 'redux-waiter'

dispatch(
  prepareWaiter('get-toy', {
    requestCreator: (params) => getToyPromise(params.id),
    params: { id: '1'}
  })
)

// then somewhere else you can call it
dispatch(callWaiter('get-toy'))

clearWaiter(waiterName)

Clear will reset the waiter as if it was never called. This removes all params, response, and error data. The waiter stays in the store and can be used again.

import { clearWaiter } from 'redux-waiter';

// In your redux environment
dispatch(clearWaiter('waiter-name'));

clearAll()

Clear all the waiters in the store.

import { clearAll } from 'redux-waiter';

// In you redux environment
dispatch(clearAll());

destroyWaiter(waiterName)

Destroy will remove the waiter from the store. It will not longer be accessible unless initialized again.

import { destroyWaiter } from 'redux-waiter';

// In you redux environment
dispatch(destroyWaiter('waiter-name'));

destroyAll()

Destroy all the waiters in the store.

import { destroyAll } from 'redux-waiter';

// In you redux environment
dispatch(destroyAll());

Selectors

getWaiter(state, waiterName)

Get the waiter model from the store by it's name.

import { getWaiter } from 'redux-waiter';

// In your mapStateToProps somewhere
(state) => getWaiter(state, 'get-toy');

getWaiterResponse(state, waiterName)

Get only the response of the promise by the waiter name. Returns null if no response has been set.

import { getWaiterResponse } from 'redux-waiter';

// In your mapStateToProps somewhere
(state) => getWaiterResponse(state, 'get-toy');

getWaiterError(state, waiterName)

Get only the error of the promise by the waiter name. Returns null if no error has been set.

import { getWaiterError } from 'redux-waiter';

// In your mapStateToProps somewhere
(state) => getWaiterError(state, 'get-toy');

Example

Valid Document Link

Create your component with the connectWaiter

import React from 'react';
import axios from 'axios';

import { connectWaiter } from 'redux-waiter';

const ValidUrlLink = connectWaiter({
  // dynamic waiter name using props
  name: (props) => `link:${props.url}`,

  requestCreator: (params) => axios({ url: params.url }),
  requestOnMountParams: (props) => ({ url: props.url }),

  // alternate views for the Promise lifecycle
  pendingView: () => <span>...</span>,
  rejectedView: () => <span>Invalid link</span>,
})((props) => <a href={props.url}>Click to View</a>);

Implement your component in the JSX

... <ValidUrlLink url="https://link.to.pdf" />' ...

Dependencies

Due to the asynchronous nature of this library, redux-waiter requires that you have redux-thunk configured as middleware for your store.