Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/diegohaz/redux-saga-thunk

Dispatching an action handled by redux-saga returns promise
https://github.com/diegohaz/redux-saga-thunk

flux-standard-action redux redux-middleware redux-reducers redux-saga redux-thunk selectors

Last synced: about 1 month ago
JSON representation

Dispatching an action handled by redux-saga returns promise

Awesome Lists containing this project

README

        

# redux-saga-thunk

[![Generated with nod](https://img.shields.io/badge/generator-nod-2196F3.svg?style=flat-square)](https://github.com/diegohaz/nod)
[![NPM version](https://img.shields.io/npm/v/redux-saga-thunk.svg?style=flat-square)](https://npmjs.org/package/redux-saga-thunk)
[![NPM downloads](https://img.shields.io/npm/dm/redux-saga-thunk.svg?style=flat-square)](https://npmjs.org/package/redux-saga-thunk)
[![Build Status](https://img.shields.io/travis/diegohaz/redux-saga-thunk/master.svg?style=flat-square)](https://travis-ci.org/diegohaz/redux-saga-thunk) [![Coverage Status](https://img.shields.io/codecov/c/github/diegohaz/redux-saga-thunk/master.svg?style=flat-square)](https://codecov.io/gh/diegohaz/redux-saga-thunk/branch/master)

Dispatching an action handled by [redux-saga](https://github.com/redux-saga/redux-saga) returns promise. It looks like [redux-thunk](https://github.com/gaearon/redux-thunk), but with pure action creators.

```js
class MyComponent extends React.Component {
componentWillMount() {
   // `doSomething` dispatches an action which is handled by some saga
   this.props.doSomething().then((detail) => {
     console.log('Yaay!', detail)
   }).catch((error) => {
     console.log('Oops!', error)
})
}
}
```

> `redux-saga-thunk` uses [Flux Standard Action](https://github.com/acdlite/flux-standard-action) to determine action's `payload`, `error` etc.






If you find this useful, please don't forget to star ⭐️ the repo, as this will help to promote the project.

Follow me on Twitter and GitHub to keep updated about this project and others.





## Motivation

There are two reasons I created this library: Server Side Rendering and [redux-form](https://github.com/erikras/redux-form).

When using [redux-saga](https://github.com/redux-saga/redux-saga) on server, you will need to know when your actions have been finished so you can send the response to the client. There are several ways to handle that case, and `redux-saga-thunk` approach is the one I like most. See [an example](https://github.com/diegohaz/arc/blob/d194b7e9578bdf3ad70a1e0d4c09ceca849f164e/src-example/containers/PostList.js#L33).

With [redux-form](https://github.com/erikras/redux-form), you need to return a promise from `dispatch` inside your submit handler so it will know when the submission is complete. See [an example](https://github.com/diegohaz/arc/blob/8d46b9e52db3f1066b124b93cf8b92d05094fe1c/src-example/containers/PostForm.js#L10)

Finally, that's a nice way to migrate your codebase from `redux-thunk` to `redux-saga`, since you will not need to change how you dispatch your actions, they will still return promises.

## Install

$ npm install --save redux-saga-thunk

## Basic setup

Add `middleware` to your redux configuration (**before redux-saga middleware**):

```js
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { middleware as thunkMiddleware } from 'redux-saga-thunk'
^

const sagaMiddleware = createSagaMiddleware()
const store = createStore({}, applyMiddleware(thunkMiddleware, sagaMiddleware))
                                             ^
```

## Usage

You just need to set `meta.thunk` to `true` on your request actions and put it on your response actions inside the saga:

```js
const action = {
 type: 'RESOURCE_REQUEST',
 payload: { id: 'foo' },
meta: {
thunk: true
   ^
 }
}

// send the action
store.dispatch(action).then((detail) => {
// payload == detail
console.log('Yaay!', detail)
}).catch((e) => {
// payload == e
console.log('Oops!', e)
})

function* saga() {
while(true) {
   const { payload, meta } = yield take('RESOURCE_REQUEST')
                    ^
  try {
     const detail = yield call(callApi, payload) // payload == { id: 'foo' }
yield put({
       type: 'RESOURCE_SUCCESS',
payload: detail,
meta
       ^
     })
} catch (e) {
yield put({
type: 'RESOURCE_FAILURE',
payload: e,
error: true,
       ^
       meta
       ^
})
}
}
}
```

`redux-saga-thunk` will automatically transform your request action and inject a `key` into it.

You can also use it inside sagas with [`put.resolve`](https://redux-saga.js.org/docs/api/#putresolveaction):

```js
function *someSaga() {
try {
const detail = yield put.resolve(action)
   console.log('Yaay!', detail)
} catch (error) {
   console.log('Oops!', error)
}
}
```

## Usage with selectors

To use `pending`, `rejected`, `fulfilled` and `done` selectors, you'll need to add the `thunkReducer` to your store:

```js
import { combineReducers } from 'redux'
import { reducer as thunkReducer } from 'redux-saga-thunk'

const reducer = combineReducers({
thunk: thunkReducer,
// your reducers...
})
```

Now you can use selectors on your containers:

```js
import { pending, rejected, fulfilled, done } from 'redux-saga-thunk'

const mapStateToProps = state => ({
loading: pending(state, 'RESOURCE_CREATE_REQUEST'),
error: rejected(state, 'RESOURCE_CREATE_REQUEST'),
success: fulfilled(state, 'RESOURCE_CREATE_REQUEST'),
done: done(state, 'RESOURCE_CREATE_REQUEST'),
})
```

## API

#### Table of Contents

- [clean](#clean)
- [pending](#pending)
- [rejected](#rejected)
- [fulfilled](#fulfilled)
- [done](#done)

### clean

Clean state

**Parameters**

- `name` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `id` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))**

**Examples**

```javascript
const mapDispatchToProps = (dispatch, ownProps) => ({
cleanFetchUserStateForAllIds: () => dispatch(clean('FETCH_USER')),
cleanFetchUserStateForSpecifiedId: () => dispatch(clean('FETCH_USER', ownProps.id)),
cleanFetchUsersState: () => dispatch(clean('FETCH_USERS')),
})
```

### pending

Tells if an action is pending

**Parameters**

- `state` **State**
- `name` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))])>)**
- `id` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))**

**Examples**

```javascript
const mapStateToProps = state => ({
fooIsPending: pending(state, 'FOO'),
barForId42IsPending: pending(state, 'BAR', 42),
barForAnyIdIsPending: pending(state, 'BAR'),
fooOrBazIsPending: pending(state, ['FOO', 'BAZ']),
fooOrBarForId42IsPending: pending(state, ['FOO', ['BAR', 42]]),
anythingIsPending: pending(state)
})
```

Returns **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**

### rejected

Tells if an action was rejected

**Parameters**

- `state` **State**
- `name` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))])>)**
- `id` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))**

**Examples**

```javascript
const mapStateToProps = state => ({
fooWasRejected: rejected(state, 'FOO'),
barForId42WasRejected: rejected(state, 'BAR', 42),
barForAnyIdWasRejected: rejected(state, 'BAR'),
fooOrBazWasRejected: rejected(state, ['FOO', 'BAZ']),
fooOrBarForId42WasRejected: rejected(state, ['FOO', ['BAR', 42]]),
anythingWasRejected: rejected(state)
})
```

Returns **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**

### fulfilled

Tells if an action is fulfilled

**Parameters**

- `state` **State**
- `name` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))])>)**
- `id` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))**

**Examples**

```javascript
const mapStateToProps = state => ({
fooIsFulfilled: fulfilled(state, 'FOO'),
barForId42IsFulfilled: fulfilled(state, 'BAR', 42),
barForAnyIdIsFulfilled: fulfilled(state, 'BAR'),
fooOrBazIsFulfilled: fulfilled(state, ['FOO', 'BAZ']),
fooOrBarForId42IsFulfilled: fulfilled(state, ['FOO', ['BAR', 42]]),
anythingIsFulfilled: fulfilled(state)
})
```

Returns **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**

### done

Tells if an action is done

**Parameters**

- `state` **State**
- `name` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)<([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | \[[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), ([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))])>)**
- `id` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) \| [number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number))**

**Examples**

```javascript
const mapStateToProps = state => ({
fooIsDone: done(state, 'FOO'),
barForId42IsDone: done(state, 'BAR', 42),
barForAnyIdIsDone: done(state, 'BAR'),
fooOrBazIsDone: done(state, ['FOO', 'BAZ']),
fooOrBarForId42IsDone: done(state, ['FOO', ['BAR', 42]]),
anythingIsDone: done(state)
})
```

Returns **[boolean](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean)**

## License

MIT © [Diego Haz](https://github.com/diegohaz)