https://github.com/ryym/redux-dutiful-thunk
Typescript friendly redux-thunk
https://github.com/ryym/redux-dutiful-thunk
redux-thunk typescript
Last synced: about 2 months ago
JSON representation
Typescript friendly redux-thunk
- Host: GitHub
- URL: https://github.com/ryym/redux-dutiful-thunk
- Owner: ryym
- License: mit
- Created: 2018-03-23T04:54:13.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2019-06-10T12:57:21.000Z (almost 6 years ago)
- Last Synced: 2025-02-20T20:17:27.278Z (2 months ago)
- Topics: redux-thunk, typescript
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/redux-dutiful-thunk
- Size: 22.5 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Redux Dutiful Thunk
[](https://www.npmjs.com/package/redux-dutiful-thunk)
[](https://circleci.com/gh/ryym/redux-dutiful-thunk)This is a [Redux](https://redux.js.org/) middleware almost the same as [redux-thunk](https://github.com/gaearon/redux-thunk), but it respects Redux types.
## Motivation
In Redux Thunk, you need to pass a thunk function directly to `dispatch` to invoke it.
```js
// 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.```js
// 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](https://www.typescriptlang.org/),
you need to tweak Redux type definitions in some way
(I don't know about [flow](https://flow.org/) 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.```js
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.
```diff
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.
```js
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.
```js
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.```typescript
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`.```typescript
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 = ThunkAction;
// A thunk action creator.
function fetchUser(id: number): Thunk {
// 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.```js
const user = await dispatch(fetchUser(id));
console.log('got user', user);
```But this cannot be done in Redux Dutiful Thunk.
```js
// 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.```js
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.```typescript
import {thunkAs} from 'redux-dutiful-thunk';function fetchUser(id: number): Thunk {
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`
```typescript
(dispatch: Dispatch, getState: () => State, context: Context) => R
```- `ThunkAction`
```typescript
{
type: string,
thunk: Thunk,
promise: Promise,
thunkType: T,
}
```### `thunk`
```typescript
(f: Thunk) => ThunkAction
```### `thunkAs`
```typescript
(type: T, f: Thunk) => ThunkAction
```### `isThunkAction`
```typescript
(action: AnyAction) => action is AnyThunkAction
```### `createThunkMiddleware`
```typescript
(contxt?: C) => Middleware
```See [src/index.ts](src/index.ts) for details.