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-dutiful-thunk

v1.1.1

Published

Use thunk action without breaking Redux types

Downloads

2

Readme

Redux Dutiful Thunk

npm version circleci

This is a Redux middleware almost the same as redux-thunk, but it respects Redux types.

Motivation

In Redux Thunk, you need to pass a thunk function directly to dispatch to invoke it.

// An action creator for Redux Thunk.
function incrementAsync() {
  return dispatch => {
    setTimeout(() => dispatch(increment()), 1000);
  };
}

// Dispatch a function.
store.dispatch(incrementAsync());

But this becomes a problem if you want to write code in a type-safe manner because the dispatch function is supposed to accept Redux actions, not functions.

// A Redux action consists of a unique `type` and zero or more extra arguments.
{ type: 'FETCH_USER', id: 30 }

So for example when you use Redux Thunk with TypeScript, you need to tweak Redux type definitions in some way (I don't know about flow much but I guess a similar problem exists). Therefore I could not find a good reason to use a function as a special action. Instead, let's create a normal action to meet the Redux rule and match its type definitions.

import {thunk} from 'redux-dutiful-thunk';

function incrementAsync() {
  // Wrap your thunk function by `thunk`.
  return thunk(async dispatch => {
      setTimeout(() => dispatch(increment()), 1000);
  });
}

// Now the action creator returns a normal action which contains a function you passed.
console.log(incrementAsync());
//=> { type: '@@redux-dutiful-thunk/THUNK', thunk: f, }

// So the `dispatch` function can take an action as usual, instead of a function.
store.dispatch(incrementAsync());

The difference with Redux Thunk is only the thunk wrapping.

 function incrementAsync() {
-  return dispatch => {
+  return thunk(async dispatch => {
       setTimeout(() => dispatch(increment()), 1000);
-  };
+  });
 }

Installation

npm install redux-dutiful-thunk

Usage

To enable Redux Dutiful Thunk, create a middleware and apply it to your store.

import {createStore, applyMiddleware} from 'redux';
import {createThunkMiddleware} from 'redux-dutiful-thunk';
import rootReducer from './reducers/index';

const store = createStore(
  rootReducer,
  applyMiddleware(createThunkMiddleware())
);

Custom Argument

Like Redux Thunk, you can inject a custom argument to your thunk actions.

const store = createStore(
  rootReducer,
  applyMiddleware(createThunkMiddleware(api))
);

function fetchUser(id) {
  return thunk(async (dispatch, getState, api) => {
    const user = await api.fetchUser(id);
    dispatch({type: 'FETCH_USER_SUCCESS', user});
  });
}

With TypeScript

This library is written in TypeScript so the type definitions are provided.

To make your dispatch function accept any thunk actions, add AnyThunkAction to your action type.

import {AnyThunkAction} from 'redux-dutiful-thunk';

type Action =
  | AnyThunkAction
  | {type: 'FETCH_USER'; id: number}
  | {type: 'DO_SOMETHING'};

To implement your thunk action creators easily, we recommend to define a Thunk type using ThunkAction.

import {thunk, ThunkAction} from 'redux-dutiful-thunk';
import {Action} from './action-type';
import {State} from './state';
import {API} from './api';
import {User} from './models'

type Thunk<R = void> = ThunkAction<State, Action, API, R>;

// A thunk action creator.
function fetchUser(id: number): Thunk<User> {
  // You can omit argument types.
  return thunk(async (dispatch, getState, api) => {
    return await api.fetchUser(id);
  });
}

Thunk's Return Value

Because the dispatch function returns a given value as is, you can get a return value of thunk function in Redux Thunk.

const user = await dispatch(fetchUser(id));
console.log('got user', user);

But this cannot be done in Redux Dutiful Thunk.

// fetchUser returns an action, not a user data.
const action = dispatch(fetchUser(id));

For this use case, thunk actions have a promise that is resolved to a return value of your thunk function.

const action = dispatch(fetchUser(id));
const user = await action.promise;

// So you can write like this
user = await dispatch(fetchUser(id)).promise;

Of course this promise is type safe.

Specifying Thunk Type

All thunk actions have a same action type. If you want to distinguish each thunk action, use thunkAs instead of thunk. This allows you to specify a thunk type.

import {thunkAs} from 'redux-dutiful-thunk';

function fetchUser(id: number): Thunk<User> {
  return thunkAs('FETCH_USER', async (dispatch, getState, api) => {
    return await api.fetchUser(id);
  });
}

const action = fetchUser(3);
console.log(action.thunkType === 'FETCH_USER'); //=> true

API

Base types

  • Thunk<State, Action, Context, R>

    (dispatch: Dispatch<Action>, getState: () => State, context: Context) => R
  • ThunkAction<State, Action, Context, R, T>

    {
      type: string,
      thunk: Thunk<State, Action, Context, R>,
      promise: Promise<R>,
      thunkType: T,
    }

thunk

(f: Thunk<S, A, C, R>) => ThunkAction<S, A, C, R, null>

thunkAs

(type: T, f: Thunk<S, A, C, R>) => ThunkAction<S, A, C, R, T>

isThunkAction

(action: AnyAction) => action is AnyThunkAction

createThunkMiddleware

(contxt?: C) => Middleware

See src/index.ts for details.