redux-saga-thunk
Version:
Dispatching an action handled by redux-saga returns promise
288 lines (216 loc) • 11.8 kB
Markdown
# redux-saga-thunk
[](https://github.com/diegohaz/nod)
[](https://npmjs.org/package/redux-saga-thunk)
[](https://npmjs.org/package/redux-saga-thunk)
[](https://travis-ci.org/diegohaz/redux-saga-thunk) [](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.
<br>
<hr>
<p align="center">
If you find this useful, please don't forget to star ⭐️ the repo, as this will help to promote the project.<br>
Follow me on <a href="https://twitter.com/diegohaz">Twitter</a> and <a href="https://github.com/diegohaz">GitHub</a> to keep updated about this project and <a href="https://github.com/diegohaz?tab=repositories">others</a>.
</p>
<hr>
<br>
## 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
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
#### 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)