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 🙏

© 2025 – Pkg Stats / Ryan Hefner

redux-catch-promise

v1.0.3

Published

Catch-promise middleware for Redux

Downloads

91

Readme

redux-catch-promise

Async thunk middleware for Redux. Write your async actions in a few lines.

Extremely useful for server-side rendering React components with asynchronous loaded state. See example below.

What’s a thunk?!

A thunk is a function that wraps an expression to delay its evaluation.

// calculation of 1 + 2 is immediate
// x === 3
let x = 1 + 2;

// calculation of 1 + 2 is delayed
// foo can be called later to perform the calculation
// foo is a thunk!
let foo = () => 1 + 2;

Motivation

redux-catch-promise middleware allows you to write action creators that returns sync or async functions instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState() as parameters.

An action creator that returns an async functions to perform asynchronous dispatch:

const SHOW_USER_LOCATION = 'SHOW_USER_LOCATION';

function showUserLocation(location) {
  return {
    type: SHOW_USER_LOCATION,
    location
  };
}

function requestUserLocation(userName) {
  return dispatch => async function () {
    const finalURL = 'https://api.github.com/users/' + userName;
    const response = await fetch(URL, {
      method: 'POST'
    });
    const data = await response.json();
    showUserLocation(data['location']);
  };
}

Installation

Upgrade redux-thunk to redux-catch-promise

If you use redux-thunk to enable async actions in a way above you have to replace redux-thunk to redux-catch-promise.

Just do it in 3 steps:

  1. npm install redux-catch-promise --save
  2. Replace import declaration:
import thunk from 'redux-thunk';

to

import CatchPromise from 'redux-catch-promise';
  1. Replace middleware assignment, i.e:
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);

to

const actionPromises = [];
const catchPromise = CatchPromise();
const createStoreWithMiddleware = applyMiddleware(catchPromise)(createStore);

Clean install

  1. npm install redux-catch-promise --save
  2. Add import declaration:
import CatchPromise from 'redux-catch-promise';
  1. Add middleware assignment, i.e:
const actionPromises = [];
const catchPromise = CatchPromise();
const createStoreWithMiddleware = applyMiddleware(catchPromise)(createStore);

Server-side Rendering with async state

import { createStore, applyMiddleware, combineReducers } from 'redux';
import catchPromise from 'redux-catch-promise';
import * as reducers from './reducers/index';

const reducer = combineReducers(reducers);

// create a store that has redux-thunk middleware enabled
const actionPromises = [];
const createStoreWithMiddleware = applyMiddleware(
  thunk,
  catchPromise((promisedAction, action, store) => {
    // it calls only when a Promise found
    actionPromises.push(promisedAction);
  }
)(createStore);

const store = createStoreWithMiddleware(reducer);

Server-side rendering with async state

It's a short demo how to implement with this middleware server-side rendering of your React components with async-loading state:

server.js

const React from 'react';
const Application from './application';
const koa = require('koa');
const app = koa();

app.use(function *(next) {
  const preparePromises = [];
  const serverSideRendering = {
    preparePromises,
    sharedState: {}
  };
  const application = (
    <Application serverSideRendering={serverSideRendering} />
  );
  const prefetchedBody = React.renderToString(application);
  if (preparePromises.length > 0) {
    for (let index = 0, length = preparePromises.length; index < length; index++) {
      yield preparePromises[index];
    }
  }
  // re-render with fetched data if prepare promises are found
  let body;
  if (serverSideRendering.preparePromises.length > 0) {
    body = React.renderToString(
      application
    );
  } else {
    body = prefetchedBody;
  }
  const code = 'window._sharedData = ' + JSON.stringify(sharedData) + ';';
  yield this.render('react-page', {
    'body': body,
    'code': code
  })
});

client.js

import 'isomorphic-fetch';
import React from 'react';
import Application from './application';

const state = (window._sharedData && window._sharedData['state']) || {};
const rootElement = document.getElementById('root');

React.render(<Application state={state} />, rootElement);

application.js

import React, { Component, PropTypes } from 'react';
import { combineReducers, createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import CatchPromise from 'redux-catch-promise';
import ReposList from './repos-list';
import reposListReducer from './repos-list/reducer';

export default class Application extends Component {
  static propTypes = {
    state: PropTypes.object,
    serverSideRendering: PropTypes.object
  };

  constructor(props, context) {
    super(props, context);
    const { serverSideRendering } = props;
    const catchPromise = CatchPromise(
      (typeof serverSideRendering === 'object') &&
        (promisedAction, action, store) => {
          serverSideRendering.preparePromises.push(promisedAction);
          serverSideRendering.sharedState = store.getState();
        });
    const createStoreWithMiddleware = applyMiddleware(catchPromise)(createStore);
    const allReducers = combineReducers({
      repostList: repostListReducer
    });
    const store = createStoreWithMiddleware(allReducers, props.state || {});
    if (typeof serverSideRendering === 'object') {
      // callback to dispatch passed actions
      this.prepare = (actions) => actions.forEach((action) => store.dispatch(action));
    }
  }
  
  render() {
    return (
      <Provider store={this.state.store}>
        {this.renderChild.bind(this)}
      </Provider>
    );
  }
  
  renderChild() {
    return (
      <ReposList prepare={this.prepare} />
    );
  }
}

repos-list/index.js

import React, { Component, PropTypes } from 'react';
import { bindActionCreators, connect } from 'react-redux';
import * as projectsListActions from './actions';

function selector(state) {
  return {
    reposList: state.reposList
  };
}

class ReposList extends Component {
  constructor(props, context) {
    super(props, context);
    this.prepareActions = [
      reposListActions.fetch()
    ];
    if (typeof props.prepare === 'function') {
      props.prepare(this.prepareActions);
    }
  }
  
  render() {
    const { reposList } = this.props;
    return (
      <ul>
        {Array.isArray(reposList.items) ?
          reposList.items.map((it) => <li>{it['name']}</li>) :
          <li>Empty</li>}
      </ul>
    );
  }
  
  componentDidMount() {
    if (this.prepareDataActions) {
      const props = this.props;
      this.prepareDataActions.forEach((action) => props.dispatch(action));
    }
  }
}

repos-list/action-types.js

export default {
  UPDATE: 'REPOS_LIST_UPDATE'
};

repos-list/actions.js

import {
  UPDATE
} from './action-types';

export default function fetch (className, force) {
  return async (dispatch, getState) => {
    try {
      const data = await fetch('http://api.github.com/users/DenisIzmaylov/repos');
      const items = await data.json();
    } catch(err) {
      console.error(err);
    }
    dispatch({
      type: UPDATE,
      state: { items }
    });
  }
}

repos-list/reducer.js

import {
  UPDATE
} from './action-types';

const initialState = {
  items: {}
};

export default function (state = initialState, action = {}) {
  switch (action.type) {
    case UPDATE:
      return [action.state, ...state];
    default:
      return state;
  }
}

Thanks

License

MIT